mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-15 12:33:45 +09:00
Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09b76295f1 | ||
|
|
38acce2f3f | ||
|
|
8f44d00f22 | ||
|
|
4386eb751f | ||
|
|
d6aab069ed | ||
|
|
f4fb8dbc87 | ||
|
|
c7c18e0eb2 | ||
|
|
0a2d618d85 | ||
|
|
c8a83ace59 | ||
|
|
59d132f0b3 | ||
|
|
18dd49a4ab | ||
|
|
46637b1164 | ||
|
|
7b18c67ac9 | ||
|
|
6eb3c05cb7 | ||
|
|
82f24bedc2 | ||
|
|
88da50674f | ||
|
|
35a7db49b4 | ||
|
|
f4729e2418 | ||
|
|
f7330fd027 | ||
|
|
755d8e21ad | ||
|
|
7c0bf06d96 | ||
|
|
0d196e29e8 | ||
|
|
b86606fa38 | ||
|
|
74602bb487 | ||
|
|
1465e0cbb2 | ||
|
|
928b603d19 | ||
|
|
8ff542c1a2 | ||
|
|
39a0db6ecf | ||
|
|
9cc93c05cd | ||
|
|
b31418edd9 | ||
|
|
242f7f1a52 | ||
|
|
8d7f1e430a | ||
|
|
a6b32adc45 | ||
|
|
1f0dca4614 | ||
|
|
1d665da32f | ||
|
|
09adc26eb6 | ||
|
|
297346a762 | ||
|
|
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 |
111
.drone.yml
111
.drone.yml
@@ -25,7 +25,7 @@ steps:
|
|||||||
- make deps-frontend
|
- make deps-frontend
|
||||||
|
|
||||||
- name: deps-backend
|
- name: deps-backend
|
||||||
image: golang:1.17
|
image: golang:1.18
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
- make deps-backend
|
- make deps-backend
|
||||||
@@ -45,32 +45,41 @@ steps:
|
|||||||
commands:
|
commands:
|
||||||
- make lint-backend
|
- make lint-backend
|
||||||
environment:
|
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
|
GOSUMDB: sum.golang.org
|
||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
TAGS: bindata sqlite sqlite_unlock_notify
|
||||||
depends_on: [deps-backend]
|
depends_on: [deps-backend]
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /go
|
||||||
|
|
||||||
- name: lint-backend-windows
|
- name: lint-backend-windows
|
||||||
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
||||||
commands:
|
commands:
|
||||||
- make golangci-lint vet
|
- make golangci-lint-windows vet
|
||||||
environment:
|
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
|
GOSUMDB: sum.golang.org
|
||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
TAGS: bindata sqlite sqlite_unlock_notify
|
||||||
GOOS: windows
|
GOOS: windows
|
||||||
GOARCH: amd64
|
GOARCH: amd64
|
||||||
depends_on: [deps-backend]
|
depends_on: [deps-backend]
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /go
|
||||||
|
|
||||||
- name: lint-backend-gogit
|
- name: lint-backend-gogit
|
||||||
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
||||||
commands:
|
commands:
|
||||||
- make lint-backend
|
- make lint-backend
|
||||||
environment:
|
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
|
GOSUMDB: sum.golang.org
|
||||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||||
depends_on: [deps-backend]
|
depends_on: [deps-backend]
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /go
|
||||||
|
|
||||||
- name: checks-frontend
|
- name: checks-frontend
|
||||||
image: node:16
|
image: node:16
|
||||||
@@ -79,7 +88,7 @@ steps:
|
|||||||
depends_on: [deps-frontend]
|
depends_on: [deps-frontend]
|
||||||
|
|
||||||
- name: checks-backend
|
- name: checks-backend
|
||||||
image: golang:1.17
|
image: golang:1.18
|
||||||
commands:
|
commands:
|
||||||
- make checks-backend
|
- make checks-backend
|
||||||
depends_on: [deps-backend]
|
depends_on: [deps-backend]
|
||||||
@@ -104,7 +113,7 @@ steps:
|
|||||||
pull: always
|
pull: always
|
||||||
environment:
|
environment:
|
||||||
GO111MODULE: on
|
GO111MODULE: on
|
||||||
GOPROXY: https://goproxy.cn
|
GOPROXY: https://goproxy.io
|
||||||
commands:
|
commands:
|
||||||
- go build -o gitea_no_gcc # test if build succeeds without the sqlite tag
|
- go build -o gitea_no_gcc # test if build succeeds without the sqlite tag
|
||||||
depends_on: [deps-backend, checks-backend]
|
depends_on: [deps-backend, checks-backend]
|
||||||
@@ -113,10 +122,10 @@ steps:
|
|||||||
path: /go
|
path: /go
|
||||||
|
|
||||||
- name: build-backend-arm64
|
- name: build-backend-arm64
|
||||||
image: golang:1.17
|
image: golang:1.18
|
||||||
environment:
|
environment:
|
||||||
GO111MODULE: on
|
GO111MODULE: on
|
||||||
GOPROXY: https://goproxy.cn
|
GOPROXY: https://goproxy.io
|
||||||
GOOS: linux
|
GOOS: linux
|
||||||
GOARCH: arm64
|
GOARCH: arm64
|
||||||
TAGS: bindata gogit
|
TAGS: bindata gogit
|
||||||
@@ -129,10 +138,10 @@ steps:
|
|||||||
path: /go
|
path: /go
|
||||||
|
|
||||||
- name: build-backend-windows
|
- name: build-backend-windows
|
||||||
image: golang:1.17
|
image: golang:1.18
|
||||||
environment:
|
environment:
|
||||||
GO111MODULE: on
|
GO111MODULE: on
|
||||||
GOPROXY: https://goproxy.cn
|
GOPROXY: https://goproxy.io
|
||||||
GOOS: windows
|
GOOS: windows
|
||||||
GOARCH: amd64
|
GOARCH: amd64
|
||||||
TAGS: bindata gogit
|
TAGS: bindata gogit
|
||||||
@@ -144,10 +153,10 @@ steps:
|
|||||||
path: /go
|
path: /go
|
||||||
|
|
||||||
- name: build-backend-386
|
- name: build-backend-386
|
||||||
image: golang:1.17
|
image: golang:1.18
|
||||||
environment:
|
environment:
|
||||||
GO111MODULE: on
|
GO111MODULE: on
|
||||||
GOPROXY: https://goproxy.cn
|
GOPROXY: https://goproxy.io
|
||||||
GOOS: linux
|
GOOS: linux
|
||||||
GOARCH: 386
|
GOARCH: 386
|
||||||
commands:
|
commands:
|
||||||
@@ -226,6 +235,7 @@ steps:
|
|||||||
image: docker:git
|
image: docker:git
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
|
- git config --global --add safe.directory /drone/src
|
||||||
- git fetch --tags --force
|
- git fetch --tags --force
|
||||||
when:
|
when:
|
||||||
event:
|
event:
|
||||||
@@ -233,7 +243,7 @@ steps:
|
|||||||
- pull_request
|
- pull_request
|
||||||
|
|
||||||
- name: deps-backend
|
- name: deps-backend
|
||||||
image: golang:1.17
|
image: golang:1.18
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
- make deps-backend
|
- make deps-backend
|
||||||
@@ -260,7 +270,7 @@ steps:
|
|||||||
- ./build/test-env-check.sh
|
- ./build/test-env-check.sh
|
||||||
- make backend
|
- make backend
|
||||||
environment:
|
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
|
GOSUMDB: sum.golang.org
|
||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
TAGS: bindata sqlite sqlite_unlock_notify
|
||||||
depends_on: [deps-backend, prepare-test-env]
|
depends_on: [deps-backend, prepare-test-env]
|
||||||
@@ -274,7 +284,7 @@ steps:
|
|||||||
commands:
|
commands:
|
||||||
- make unit-test-coverage test-check
|
- make unit-test-coverage test-check
|
||||||
environment:
|
environment:
|
||||||
GOPROXY: https://goproxy.cn
|
GOPROXY: https://goproxy.io
|
||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
TAGS: bindata sqlite sqlite_unlock_notify
|
||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
GITHUB_READ_TOKEN:
|
GITHUB_READ_TOKEN:
|
||||||
@@ -290,7 +300,7 @@ steps:
|
|||||||
commands:
|
commands:
|
||||||
- make unit-test-coverage test-check
|
- make unit-test-coverage test-check
|
||||||
environment:
|
environment:
|
||||||
GOPROXY: https://goproxy.cn
|
GOPROXY: https://goproxy.io
|
||||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
GITHUB_READ_TOKEN:
|
GITHUB_READ_TOKEN:
|
||||||
@@ -306,7 +316,7 @@ steps:
|
|||||||
commands:
|
commands:
|
||||||
- make test-mysql-migration integration-test-coverage
|
- make test-mysql-migration integration-test-coverage
|
||||||
environment:
|
environment:
|
||||||
GOPROXY: https://goproxy.cn
|
GOPROXY: https://goproxy.io
|
||||||
TAGS: bindata
|
TAGS: bindata
|
||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
TEST_LDAP: 1
|
TEST_LDAP: 1
|
||||||
@@ -323,7 +333,7 @@ steps:
|
|||||||
commands:
|
commands:
|
||||||
- timeout -s ABRT 40m make test-mysql8-migration test-mysql8
|
- timeout -s ABRT 40m make test-mysql8-migration test-mysql8
|
||||||
environment:
|
environment:
|
||||||
GOPROXY: https://goproxy.cn
|
GOPROXY: https://goproxy.io
|
||||||
TAGS: bindata
|
TAGS: bindata
|
||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
TEST_LDAP: 1
|
TEST_LDAP: 1
|
||||||
@@ -339,7 +349,7 @@ steps:
|
|||||||
commands:
|
commands:
|
||||||
- make test-mssql-migration test-mssql
|
- make test-mssql-migration test-mssql
|
||||||
environment:
|
environment:
|
||||||
GOPROXY: https://goproxy.cn
|
GOPROXY: https://goproxy.io
|
||||||
TAGS: bindata
|
TAGS: bindata
|
||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
TEST_LDAP: 1
|
TEST_LDAP: 1
|
||||||
@@ -350,11 +360,11 @@ steps:
|
|||||||
path: /go
|
path: /go
|
||||||
|
|
||||||
- name: generate-coverage
|
- name: generate-coverage
|
||||||
image: golang:1.17
|
image: golang:1.18
|
||||||
commands:
|
commands:
|
||||||
- make coverage
|
- make coverage
|
||||||
environment:
|
environment:
|
||||||
GOPROXY: https://goproxy.cn
|
GOPROXY: https://goproxy.io
|
||||||
TAGS: bindata
|
TAGS: bindata
|
||||||
depends_on: [unit-test, test-mysql]
|
depends_on: [unit-test, test-mysql]
|
||||||
when:
|
when:
|
||||||
@@ -418,6 +428,7 @@ steps:
|
|||||||
image: docker:git
|
image: docker:git
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
|
- git config --global --add safe.directory /drone/src
|
||||||
- git fetch --tags --force
|
- git fetch --tags --force
|
||||||
when:
|
when:
|
||||||
event:
|
event:
|
||||||
@@ -425,7 +436,7 @@ steps:
|
|||||||
- pull_request
|
- pull_request
|
||||||
|
|
||||||
- name: deps-backend
|
- name: deps-backend
|
||||||
image: golang:1.17
|
image: golang:1.18
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
- make deps-backend
|
- make deps-backend
|
||||||
@@ -446,7 +457,7 @@ steps:
|
|||||||
- ./build/test-env-check.sh
|
- ./build/test-env-check.sh
|
||||||
- make backend
|
- make backend
|
||||||
environment:
|
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
|
GOSUMDB: sum.golang.org
|
||||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||||
depends_on: [deps-backend, prepare-test-env]
|
depends_on: [deps-backend, prepare-test-env]
|
||||||
@@ -460,7 +471,7 @@ steps:
|
|||||||
commands:
|
commands:
|
||||||
- timeout -s ABRT 40m make test-sqlite-migration test-sqlite
|
- timeout -s ABRT 40m make test-sqlite-migration test-sqlite
|
||||||
environment:
|
environment:
|
||||||
GOPROXY: https://goproxy.cn
|
GOPROXY: https://goproxy.io
|
||||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
TEST_TAGS: gogit sqlite sqlite_unlock_notify
|
TEST_TAGS: gogit sqlite sqlite_unlock_notify
|
||||||
@@ -476,7 +487,7 @@ steps:
|
|||||||
commands:
|
commands:
|
||||||
- timeout -s ABRT 40m make test-pgsql-migration test-pgsql
|
- timeout -s ABRT 40m make test-pgsql-migration test-pgsql
|
||||||
environment:
|
environment:
|
||||||
GOPROXY: https://goproxy.cn
|
GOPROXY: https://goproxy.io
|
||||||
TAGS: bindata gogit
|
TAGS: bindata gogit
|
||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
TEST_TAGS: gogit
|
TEST_TAGS: gogit
|
||||||
@@ -567,7 +578,7 @@ trigger:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: download
|
- name: download
|
||||||
image: golang:1.17
|
image: golang:1.18
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
- timeout -s ABRT 40m make generate-license generate-gitignore
|
- timeout -s ABRT 40m make generate-license generate-gitignore
|
||||||
@@ -619,6 +630,7 @@ steps:
|
|||||||
image: docker:git
|
image: docker:git
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
|
- git config --global --add safe.directory /drone/src
|
||||||
- git fetch --tags --force
|
- git fetch --tags --force
|
||||||
|
|
||||||
- name: deps-frontend
|
- name: deps-frontend
|
||||||
@@ -628,7 +640,7 @@ steps:
|
|||||||
- make deps-frontend
|
- make deps-frontend
|
||||||
|
|
||||||
- name: deps-backend
|
- name: deps-backend
|
||||||
image: golang:1.17
|
image: golang:1.18
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
- make deps-backend
|
- make deps-backend
|
||||||
@@ -637,14 +649,14 @@ steps:
|
|||||||
path: /go
|
path: /go
|
||||||
|
|
||||||
- name: static
|
- name: static
|
||||||
image: techknowlogick/xgo:go-1.17.x
|
image: techknowlogick/xgo:go-1.18.x
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
- curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs
|
- curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs
|
||||||
- export PATH=$PATH:$GOPATH/bin
|
- export PATH=$PATH:$GOPATH/bin
|
||||||
- make release
|
- make release
|
||||||
environment:
|
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
|
TAGS: bindata sqlite sqlite_unlock_notify
|
||||||
volumes:
|
volumes:
|
||||||
- name: deps
|
- name: deps
|
||||||
@@ -737,6 +749,7 @@ steps:
|
|||||||
image: docker:git
|
image: docker:git
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
|
- git config --global --add safe.directory /drone/src
|
||||||
- git fetch --tags --force
|
- git fetch --tags --force
|
||||||
|
|
||||||
- name: deps-frontend
|
- name: deps-frontend
|
||||||
@@ -746,7 +759,7 @@ steps:
|
|||||||
- make deps-frontend
|
- make deps-frontend
|
||||||
|
|
||||||
- name: deps-backend
|
- name: deps-backend
|
||||||
image: golang:1.17
|
image: golang:1.18
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
- make deps-backend
|
- make deps-backend
|
||||||
@@ -755,14 +768,14 @@ steps:
|
|||||||
path: /go
|
path: /go
|
||||||
|
|
||||||
- name: static
|
- name: static
|
||||||
image: techknowlogick/xgo:go-1.17.x
|
image: techknowlogick/xgo:go-1.18.x
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
- curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs
|
- curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs
|
||||||
- export PATH=$PATH:$GOPATH/bin
|
- export PATH=$PATH:$GOPATH/bin
|
||||||
- make release
|
- make release
|
||||||
environment:
|
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
|
TAGS: bindata sqlite sqlite_unlock_notify
|
||||||
depends_on: [fetch-tags]
|
depends_on: [fetch-tags]
|
||||||
volumes:
|
volumes:
|
||||||
@@ -882,6 +895,7 @@ steps:
|
|||||||
image: docker:git
|
image: docker:git
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
|
- git config --global --add safe.directory /drone/src
|
||||||
- git fetch --tags --force
|
- git fetch --tags --force
|
||||||
|
|
||||||
- name: publish
|
- name: publish
|
||||||
@@ -892,7 +906,7 @@ steps:
|
|||||||
auto_tag_suffix: linux-amd64
|
auto_tag_suffix: linux-amd64
|
||||||
repo: gitea/gitea
|
repo: gitea/gitea
|
||||||
build_args:
|
build_args:
|
||||||
- GOPROXY=https://goproxy.cn
|
- GOPROXY=https://goproxy.io
|
||||||
password:
|
password:
|
||||||
from_secret: docker_password
|
from_secret: docker_password
|
||||||
username:
|
username:
|
||||||
@@ -910,7 +924,7 @@ steps:
|
|||||||
auto_tag_suffix: linux-amd64-rootless
|
auto_tag_suffix: linux-amd64-rootless
|
||||||
repo: gitea/gitea
|
repo: gitea/gitea
|
||||||
build_args:
|
build_args:
|
||||||
- GOPROXY=https://goproxy.cn
|
- GOPROXY=https://goproxy.io
|
||||||
password:
|
password:
|
||||||
from_secret: docker_password
|
from_secret: docker_password
|
||||||
username:
|
username:
|
||||||
@@ -945,6 +959,7 @@ steps:
|
|||||||
image: docker:git
|
image: docker:git
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
|
- git config --global --add safe.directory /drone/src
|
||||||
- git fetch --tags --force
|
- git fetch --tags --force
|
||||||
|
|
||||||
- name: publish
|
- name: publish
|
||||||
@@ -955,7 +970,7 @@ steps:
|
|||||||
tags: dev-linux-amd64
|
tags: dev-linux-amd64
|
||||||
repo: gitea/gitea
|
repo: gitea/gitea
|
||||||
build_args:
|
build_args:
|
||||||
- GOPROXY=https://goproxy.cn
|
- GOPROXY=https://goproxy.io
|
||||||
password:
|
password:
|
||||||
from_secret: docker_password
|
from_secret: docker_password
|
||||||
username:
|
username:
|
||||||
@@ -973,7 +988,7 @@ steps:
|
|||||||
tags: dev-linux-amd64-rootless
|
tags: dev-linux-amd64-rootless
|
||||||
repo: gitea/gitea
|
repo: gitea/gitea
|
||||||
build_args:
|
build_args:
|
||||||
- GOPROXY=https://goproxy.cn
|
- GOPROXY=https://goproxy.io
|
||||||
password:
|
password:
|
||||||
from_secret: docker_password
|
from_secret: docker_password
|
||||||
username:
|
username:
|
||||||
@@ -1007,6 +1022,7 @@ steps:
|
|||||||
image: docker:git
|
image: docker:git
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
|
- git config --global --add safe.directory /drone/src
|
||||||
- git fetch --tags --force
|
- git fetch --tags --force
|
||||||
|
|
||||||
- name: publish
|
- name: publish
|
||||||
@@ -1017,7 +1033,7 @@ steps:
|
|||||||
tags: ${DRONE_BRANCH##release/v}-dev-linux-amd64
|
tags: ${DRONE_BRANCH##release/v}-dev-linux-amd64
|
||||||
repo: gitea/gitea
|
repo: gitea/gitea
|
||||||
build_args:
|
build_args:
|
||||||
- GOPROXY=https://goproxy.cn
|
- GOPROXY=https://goproxy.io
|
||||||
password:
|
password:
|
||||||
from_secret: docker_password
|
from_secret: docker_password
|
||||||
username:
|
username:
|
||||||
@@ -1035,7 +1051,7 @@ steps:
|
|||||||
tags: ${DRONE_BRANCH##release/v}-dev-linux-amd64-rootless
|
tags: ${DRONE_BRANCH##release/v}-dev-linux-amd64-rootless
|
||||||
repo: gitea/gitea
|
repo: gitea/gitea
|
||||||
build_args:
|
build_args:
|
||||||
- GOPROXY=https://goproxy.cn
|
- GOPROXY=https://goproxy.io
|
||||||
password:
|
password:
|
||||||
from_secret: docker_password
|
from_secret: docker_password
|
||||||
username:
|
username:
|
||||||
@@ -1070,7 +1086,7 @@ steps:
|
|||||||
repo: gitea/gitea
|
repo: gitea/gitea
|
||||||
tags: linux-arm64
|
tags: linux-arm64
|
||||||
build_args:
|
build_args:
|
||||||
- GOPROXY=https://goproxy.cn
|
- GOPROXY=https://goproxy.io
|
||||||
environment:
|
environment:
|
||||||
PLUGIN_MIRROR:
|
PLUGIN_MIRROR:
|
||||||
from_secret: plugin_mirror
|
from_secret: plugin_mirror
|
||||||
@@ -1103,6 +1119,7 @@ steps:
|
|||||||
image: docker:git
|
image: docker:git
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
|
- git config --global --add safe.directory /drone/src
|
||||||
- git fetch --tags --force
|
- git fetch --tags --force
|
||||||
|
|
||||||
- name: publish
|
- name: publish
|
||||||
@@ -1113,7 +1130,7 @@ steps:
|
|||||||
auto_tag_suffix: linux-arm64
|
auto_tag_suffix: linux-arm64
|
||||||
repo: gitea/gitea
|
repo: gitea/gitea
|
||||||
build_args:
|
build_args:
|
||||||
- GOPROXY=https://goproxy.cn
|
- GOPROXY=https://goproxy.io
|
||||||
password:
|
password:
|
||||||
from_secret: docker_password
|
from_secret: docker_password
|
||||||
username:
|
username:
|
||||||
@@ -1131,7 +1148,7 @@ steps:
|
|||||||
auto_tag_suffix: linux-arm64-rootless
|
auto_tag_suffix: linux-arm64-rootless
|
||||||
repo: gitea/gitea
|
repo: gitea/gitea
|
||||||
build_args:
|
build_args:
|
||||||
- GOPROXY=https://goproxy.cn
|
- GOPROXY=https://goproxy.io
|
||||||
password:
|
password:
|
||||||
from_secret: docker_password
|
from_secret: docker_password
|
||||||
username:
|
username:
|
||||||
@@ -1166,6 +1183,7 @@ steps:
|
|||||||
image: docker:git
|
image: docker:git
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
|
- git config --global --add safe.directory /drone/src
|
||||||
- git fetch --tags --force
|
- git fetch --tags --force
|
||||||
|
|
||||||
- name: publish
|
- name: publish
|
||||||
@@ -1176,7 +1194,7 @@ steps:
|
|||||||
tags: dev-linux-arm64
|
tags: dev-linux-arm64
|
||||||
repo: gitea/gitea
|
repo: gitea/gitea
|
||||||
build_args:
|
build_args:
|
||||||
- GOPROXY=https://goproxy.cn
|
- GOPROXY=https://goproxy.io
|
||||||
password:
|
password:
|
||||||
from_secret: docker_password
|
from_secret: docker_password
|
||||||
username:
|
username:
|
||||||
@@ -1194,7 +1212,7 @@ steps:
|
|||||||
tags: dev-linux-arm64-rootless
|
tags: dev-linux-arm64-rootless
|
||||||
repo: gitea/gitea
|
repo: gitea/gitea
|
||||||
build_args:
|
build_args:
|
||||||
- GOPROXY=https://goproxy.cn
|
- GOPROXY=https://goproxy.io
|
||||||
password:
|
password:
|
||||||
from_secret: docker_password
|
from_secret: docker_password
|
||||||
username:
|
username:
|
||||||
@@ -1228,6 +1246,7 @@ steps:
|
|||||||
image: docker:git
|
image: docker:git
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
|
- git config --global --add safe.directory /drone/src
|
||||||
- git fetch --tags --force
|
- git fetch --tags --force
|
||||||
|
|
||||||
- name: publish
|
- name: publish
|
||||||
@@ -1238,7 +1257,7 @@ steps:
|
|||||||
tags: ${DRONE_BRANCH##release/v}-dev-linux-arm64
|
tags: ${DRONE_BRANCH##release/v}-dev-linux-arm64
|
||||||
repo: gitea/gitea
|
repo: gitea/gitea
|
||||||
build_args:
|
build_args:
|
||||||
- GOPROXY=https://goproxy.cn
|
- GOPROXY=https://goproxy.io
|
||||||
password:
|
password:
|
||||||
from_secret: docker_password
|
from_secret: docker_password
|
||||||
username:
|
username:
|
||||||
@@ -1256,7 +1275,7 @@ steps:
|
|||||||
tags: ${DRONE_BRANCH##release/v}-dev-linux-arm64-rootless
|
tags: ${DRONE_BRANCH##release/v}-dev-linux-arm64-rootless
|
||||||
repo: gitea/gitea
|
repo: gitea/gitea
|
||||||
build_args:
|
build_args:
|
||||||
- GOPROXY=https://goproxy.cn
|
- GOPROXY=https://goproxy.io
|
||||||
password:
|
password:
|
||||||
from_secret: docker_password
|
from_secret: docker_password
|
||||||
username:
|
username:
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ linters:
|
|||||||
#- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
|
#- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
|
||||||
- gofmt
|
- gofmt
|
||||||
- misspell
|
- misspell
|
||||||
- gocritic
|
#- gocritic # TODO: disabled until fixed with go 1.18
|
||||||
- bidichk
|
- bidichk
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- revive
|
- revive
|
||||||
@@ -22,7 +22,7 @@ linters:
|
|||||||
fast: false
|
fast: false
|
||||||
|
|
||||||
run:
|
run:
|
||||||
timeout: 3m
|
timeout: 10m
|
||||||
skip-dirs:
|
skip-dirs:
|
||||||
- node_modules
|
- node_modules
|
||||||
- public
|
- public
|
||||||
@@ -61,6 +61,9 @@ linters-settings:
|
|||||||
- name: errorf
|
- name: errorf
|
||||||
- name: duplicated-imports
|
- name: duplicated-imports
|
||||||
- name: modifies-value-receiver
|
- name: modifies-value-receiver
|
||||||
|
gofumpt:
|
||||||
|
extra-rules: true
|
||||||
|
lang-version: 1.18
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
exclude-rules:
|
exclude-rules:
|
||||||
@@ -148,3 +151,11 @@ issues:
|
|||||||
- path: models/user/openid.go
|
- path: models/user/openid.go
|
||||||
linters:
|
linters:
|
||||||
- golint
|
- 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."
|
||||||
|
|||||||
106
CHANGELOG.md
106
CHANGELOG.md
@@ -4,6 +4,112 @@ This changelog goes through all the changes that have been made in each release
|
|||||||
without substantial changes to our git log; to see the highlights of what has
|
without substantial changes to our git log; to see the highlights of what has
|
||||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||||
|
|
||||||
|
## [1.16.8](https://github.com/go-gitea/gitea/releases/tag/v1.16.8) - 2022-05-16
|
||||||
|
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Add doctor check/fix for bogus action rows (#19656) (#19669)
|
||||||
|
* Make .cs highlighting legible on dark themes. (#19604) (#19605)
|
||||||
|
* BUGFIXES
|
||||||
|
* Fix oauth setting list bug (#19681)
|
||||||
|
* Delete user related oauth stuff on user deletion too (#19677) (#19680)
|
||||||
|
* Fix new release from tags list UI (#19670) (#19673)
|
||||||
|
* Prevent NPE when checking repo units if the user is nil (#19625) (#19630)
|
||||||
|
* GetFeeds must always discard actions with dangling repo_id (#19598) (#19629)
|
||||||
|
* Call MultipartForm.RemoveAll when request finishes (#19606) (#19607)
|
||||||
|
* Avoid MoreThanOne error when creating a branch whose name conflicts with other ref names (#19557) (#19591)
|
||||||
|
* Fix sending empty notifications (#19589) (#19590)
|
||||||
|
* Ignore DNS error when doing migration allow/block check (#19566) (#19567)
|
||||||
|
* Fix issue overview for teams (#19652) (#19653)
|
||||||
|
|
||||||
|
## [1.16.7](https://github.com/go-gitea/gitea/releases/tag/v1.16.7) - 2022-05-02
|
||||||
|
|
||||||
|
* SECURITY
|
||||||
|
* Escape git fetch remote (#19487) (#19490)
|
||||||
|
* BUGFIXES
|
||||||
|
* Don't overwrite err with nil (#19572) (#19574)
|
||||||
|
* On Migrations, only write commit-graph if wiki clone was successful (#19563) (#19568)
|
||||||
|
* Respect DefaultUserIsRestricted system default when creating new user (#19310) (#19560)
|
||||||
|
* Don't error when branch's commit doesn't exist (#19547) (#19548)
|
||||||
|
* Support `hostname:port` to pass host matcher's check (#19543) (#19544)
|
||||||
|
* Prevent intermittent race in attribute reader close (#19537) (#19539)
|
||||||
|
* Fix 64-bit atomic operations on 32-bit machines (#19531) (#19532)
|
||||||
|
* Prevent dangling archiver goroutine (#19516) (#19526)
|
||||||
|
* Fix migrate release from github (#19510) (#19523)
|
||||||
|
* When view _Siderbar or _Footer, just display once (#19501) (#19522)
|
||||||
|
* Fix blame page select range error and some typos (#19503)
|
||||||
|
* Fix name of doctor fix "authorized-keys" in hints (#19464) (#19484)
|
||||||
|
* User specific repoID or xorm builder conditions for issue search (#19475) (#19476)
|
||||||
|
* Prevent dangling cat-file calls (goroutine alternative) (#19454) (#19466)
|
||||||
|
* RepoAssignment ensure to close before overwrite (#19449) (#19460)
|
||||||
|
* Set correct PR status on 3way on conflict checking (#19457) (#19458)
|
||||||
|
* Mark TemplateLoading error as "UnprocessableEntity" (#19445) (#19446)
|
||||||
|
|
||||||
|
## [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
|
## [1.16.4](https://github.com/go-gitea/gitea/releases/tag/v1.16.4) - 2022-03-14
|
||||||
|
|
||||||
* SECURITY
|
* SECURITY
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
|
#Build stage
|
||||||
###################################
|
FROM golang:1.18-alpine3.15 AS build-env
|
||||||
#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
|
|
||||||
|
|
||||||
ARG GOPROXY
|
ARG GOPROXY
|
||||||
ENV GOPROXY ${GOPROXY:-direct}
|
ENV GOPROXY ${GOPROXY:-direct}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
|
#Build stage
|
||||||
###################################
|
FROM golang:1.18-alpine3.15 AS build-env
|
||||||
#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
|
|
||||||
|
|
||||||
ARG GOPROXY
|
ARG GOPROXY
|
||||||
ENV GOPROXY ${GOPROXY:-direct}
|
ENV GOPROXY ${GOPROXY:-direct}
|
||||||
|
|||||||
109
Makefile
109
Makefile
@@ -24,10 +24,17 @@ SHASUM ?= shasum -a 256
|
|||||||
HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
|
HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
|
||||||
COMMA := ,
|
COMMA := ,
|
||||||
|
|
||||||
XGO_VERSION := go-1.17.x
|
XGO_VERSION := go-1.18.x
|
||||||
MIN_GO_VERSION := 001016000
|
|
||||||
MIN_NODE_VERSION := 012017000
|
AIR_PACKAGE ?= github.com/cosmtrek/air@v1.29.0
|
||||||
MIN_GOLANGCI_LINT_VERSION := 001043000
|
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_IMAGE ?= gitea/gitea
|
||||||
DOCKER_TAG ?= latest
|
DOCKER_TAG ?= latest
|
||||||
@@ -125,8 +132,6 @@ ifeq ($(filter $(TAGS_SPLIT),bindata),bindata)
|
|||||||
GO_SOURCES += $(BINDATA_DEST)
|
GO_SOURCES += $(BINDATA_DEST)
|
||||||
endif
|
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 := templates/swagger/v1_json.tmpl
|
||||||
SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|g
|
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
|
SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|"basePath": "/api/v1"|g
|
||||||
@@ -196,10 +201,15 @@ help:
|
|||||||
|
|
||||||
.PHONY: go-check
|
.PHONY: go-check
|
||||||
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 \
|
@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; \
|
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
|
fi
|
||||||
|
|
||||||
.PHONY: git-check
|
.PHONY: git-check
|
||||||
@@ -211,11 +221,12 @@ git-check:
|
|||||||
|
|
||||||
.PHONY: node-check
|
.PHONY: node-check
|
||||||
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 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))
|
$(eval NPM_MISSING := $(shell hash npm > /dev/null 2>&1 || echo 1))
|
||||||
@if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" -o "$(NPM_MISSING)" = "1" ]; then \
|
@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; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -234,8 +245,8 @@ clean:
|
|||||||
|
|
||||||
.PHONY: fmt
|
.PHONY: fmt
|
||||||
fmt:
|
fmt:
|
||||||
@echo "Running gitea-fmt(with gofmt)..."
|
@echo "Running gitea-fmt (with gofumpt)..."
|
||||||
@$(GO) run build/code-batch-process.go gitea-fmt -s -w '{file-list}'
|
@MISSPELL_PACKAGE=$(MISSPELL_PACKAGE) GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
|
||||||
|
|
||||||
.PHONY: vet
|
.PHONY: vet
|
||||||
vet:
|
vet:
|
||||||
@@ -254,7 +265,7 @@ endif
|
|||||||
|
|
||||||
.PHONY: generate-swagger
|
.PHONY: generate-swagger
|
||||||
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_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||||
$(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
|
$(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
|
||||||
|
|
||||||
@@ -270,21 +281,18 @@ swagger-check: generate-swagger
|
|||||||
.PHONY: swagger-validate
|
.PHONY: swagger-validate
|
||||||
swagger-validate:
|
swagger-validate:
|
||||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)'
|
$(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)'
|
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||||
|
|
||||||
.PHONY: errcheck
|
.PHONY: errcheck
|
||||||
errcheck:
|
errcheck:
|
||||||
@hash errcheck > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
$(GO) install github.com/kisielk/errcheck@8ddee489636a8311a376fc92e27a6a13c6658344; \
|
|
||||||
fi
|
|
||||||
@echo "Running errcheck..."
|
@echo "Running errcheck..."
|
||||||
@errcheck $(GO_PACKAGES)
|
$(GO) run $(ERRCHECK_PACKAGE) $(GO_PACKAGES)
|
||||||
|
|
||||||
.PHONY: fmt-check
|
.PHONY: fmt-check
|
||||||
fmt-check:
|
fmt-check:
|
||||||
# get all go files and run gitea-fmt (with gofmt) on them
|
# 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 \
|
if [ -n "$$diff" ]; then \
|
||||||
echo "Please run 'make fmt' and commit the result:"; \
|
echo "Please run 'make fmt' and commit the result:"; \
|
||||||
echo "$${diff}"; \
|
echo "$${diff}"; \
|
||||||
@@ -323,10 +331,7 @@ watch-frontend: node-check node_modules
|
|||||||
|
|
||||||
.PHONY: watch-backend
|
.PHONY: watch-backend
|
||||||
watch-backend: go-check
|
watch-backend: go-check
|
||||||
@hash air > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
$(GO) run $(AIR_PACKAGE) -c .air.toml
|
||||||
$(GO) install github.com/cosmtrek/air@bedc18201271882c2be66d216d0e1a275b526ec4; \
|
|
||||||
fi
|
|
||||||
air -c .air.toml
|
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test: test-frontend test-backend
|
test: test-frontend test-backend
|
||||||
@@ -599,12 +604,9 @@ $(DIST_DIRS):
|
|||||||
|
|
||||||
.PHONY: release-windows
|
.PHONY: release-windows
|
||||||
release-windows: | $(DIST_DIRS)
|
release-windows: | $(DIST_DIRS)
|
||||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
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) .
|
||||||
$(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) .
|
|
||||||
ifeq (,$(findstring gogit,$(TAGS)))
|
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
|
endif
|
||||||
ifeq ($(CI),drone)
|
ifeq ($(CI),drone)
|
||||||
cp /build/* $(DIST)/binaries
|
cp /build/* $(DIST)/binaries
|
||||||
@@ -612,20 +614,14 @@ endif
|
|||||||
|
|
||||||
.PHONY: release-linux
|
.PHONY: release-linux
|
||||||
release-linux: | $(DIST_DIRS)
|
release-linux: | $(DIST_DIRS)
|
||||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
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) .
|
||||||
$(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) .
|
|
||||||
ifeq ($(CI),drone)
|
ifeq ($(CI),drone)
|
||||||
cp /build/* $(DIST)/binaries
|
cp /build/* $(DIST)/binaries
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: release-darwin
|
.PHONY: release-darwin
|
||||||
release-darwin: | $(DIST_DIRS)
|
release-darwin: | $(DIST_DIRS)
|
||||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
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) .
|
||||||
$(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) .
|
|
||||||
ifeq ($(CI),drone)
|
ifeq ($(CI),drone)
|
||||||
cp /build/* $(DIST)/binaries
|
cp /build/* $(DIST)/binaries
|
||||||
endif
|
endif
|
||||||
@@ -640,10 +636,7 @@ release-check: | $(DIST_DIRS)
|
|||||||
|
|
||||||
.PHONY: release-compress
|
.PHONY: release-compress
|
||||||
release-compress: | $(DIST_DIRS)
|
release-compress: | $(DIST_DIRS)
|
||||||
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && $(GO) run $(GXZ_PAGAGE) -k -9 $${file}; done;
|
||||||
$(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;
|
|
||||||
|
|
||||||
.PHONY: release-sources
|
.PHONY: release-sources
|
||||||
release-sources: | $(DIST_DIRS)
|
release-sources: | $(DIST_DIRS)
|
||||||
@@ -673,6 +666,15 @@ deps-frontend: node_modules
|
|||||||
.PHONY: deps-backend
|
.PHONY: deps-backend
|
||||||
deps-backend:
|
deps-backend:
|
||||||
$(GO) mod download
|
$(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
|
node_modules: package-lock.json
|
||||||
npm install --no-save
|
npm install --no-save
|
||||||
@@ -766,22 +768,19 @@ pr\#%: clean-all
|
|||||||
$(GO) run contrib/pr/checkout.go $*
|
$(GO) run contrib/pr/checkout.go $*
|
||||||
|
|
||||||
.PHONY: golangci-lint
|
.PHONY: golangci-lint
|
||||||
golangci-lint: golangci-lint-check
|
golangci-lint:
|
||||||
golangci-lint run --timeout 10m
|
$(GO) run $(GOLANGCI_LINT_PACKAGE) run
|
||||||
|
|
||||||
.PHONY: golangci-lint-check
|
# workaround step for the lint-backend-windows CI task because 'go run' can not
|
||||||
golangci-lint-check:
|
# have distinct GOOS/GOARCH for its build and run steps
|
||||||
$(eval GOLANGCI_LINT_VERSION := $(shell printf "%03d%03d%03d" $(shell golangci-lint --version | grep -Eo '[0-9]+\.[0-9.]+' | tr '.' ' ');))
|
.PHONY: golangci-lint-windows
|
||||||
$(eval MIN_GOLANGCI_LINT_VER_FMT := $(shell printf "%g.%g.%g" $(shell echo $(MIN_GOLANGCI_LINT_VERSION) | grep -o ...)))
|
golangci-lint-windows:
|
||||||
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
@GOOS= GOARCH= $(GO) install $(GOLANGCI_LINT_PACKAGE)
|
||||||
echo "Downloading golangci-lint v${MIN_GOLANGCI_LINT_VER_FMT}"; \
|
golangci-lint run
|
||||||
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); \
|
.PHONY: editorconfig-checker
|
||||||
elif [ "$(GOLANGCI_LINT_VERSION)" -lt "$(MIN_GOLANGCI_LINT_VERSION)" ]; then \
|
editorconfig-checker:
|
||||||
echo "Downloading newer version of golangci-lint v${MIN_GOLANGCI_LINT_VER_FMT}"; \
|
$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) templates
|
||||||
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
|
|
||||||
|
|
||||||
.PHONY: docker
|
.PHONY: docker
|
||||||
docker:
|
docker:
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ or if SQLite support is required:
|
|||||||
|
|
||||||
The `build` target is split into two sub-targets:
|
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.
|
- `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.
|
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{
|
c := exec.Cmd{
|
||||||
Path: foundCmd,
|
Path: foundCmd,
|
||||||
Args: args,
|
Args: append([]string{cmd}, args...),
|
||||||
Stdin: os.Stdin,
|
Stdin: os.Stdin,
|
||||||
Stdout: os.Stdout,
|
Stdout: os.Stdout,
|
||||||
Stderr: os.Stderr,
|
Stderr: os.Stderr,
|
||||||
@@ -270,9 +270,10 @@ func main() {
|
|||||||
if containsString(subArgs, "-w") {
|
if containsString(subArgs, "-w") {
|
||||||
cmdErrors = append(cmdErrors, giteaFormatGoImports(files))
|
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":
|
case "misspell":
|
||||||
cmdErrors = append(cmdErrors, passThroughCmd("misspell", substArgs))
|
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("MISSPELL_PACKAGE")}, substArgs...)))
|
||||||
default:
|
default:
|
||||||
log.Fatalf("unknown cmd: %s %v", subCmd, subArgs)
|
log.Fatalf("unknown cmd: %s %v", subCmd, subArgs)
|
||||||
}
|
}
|
||||||
|
|||||||
20
cmd/admin.go
20
cmd/admin.go
@@ -25,6 +25,7 @@ import (
|
|||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/storage"
|
"code.gitea.io/gitea/modules/storage"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
auth_service "code.gitea.io/gitea/services/auth"
|
auth_service "code.gitea.io/gitea/services/auth"
|
||||||
"code.gitea.io/gitea/services/auth/source/oauth2"
|
"code.gitea.io/gitea/services/auth/source/oauth2"
|
||||||
"code.gitea.io/gitea/services/auth/source/smtp"
|
"code.gitea.io/gitea/services/auth/source/smtp"
|
||||||
@@ -113,6 +114,10 @@ var (
|
|||||||
Name: "access-token",
|
Name: "access-token",
|
||||||
Usage: "Generate access token for the user",
|
Usage: "Generate access token for the user",
|
||||||
},
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "restricted",
|
||||||
|
Usage: "Make a restricted user account",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -537,17 +542,26 @@ func runCreateUser(c *cli.Context) error {
|
|||||||
changePassword = c.Bool("must-change-password")
|
changePassword = c.Bool("must-change-password")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
restricted := util.OptionalBoolNone
|
||||||
|
|
||||||
|
if c.IsSet("restricted") {
|
||||||
|
restricted = util.OptionalBoolOf(c.Bool("restricted"))
|
||||||
|
}
|
||||||
|
|
||||||
u := &user_model.User{
|
u := &user_model.User{
|
||||||
Name: username,
|
Name: username,
|
||||||
Email: c.String("email"),
|
Email: c.String("email"),
|
||||||
Passwd: password,
|
Passwd: password,
|
||||||
IsActive: true,
|
|
||||||
IsAdmin: c.Bool("admin"),
|
IsAdmin: c.Bool("admin"),
|
||||||
MustChangePassword: changePassword,
|
MustChangePassword: changePassword,
|
||||||
Theme: setting.UI.DefaultTheme,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := user_model.CreateUser(u); err != nil {
|
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
||||||
|
IsActive: util.OptionalBoolTrue,
|
||||||
|
IsRestricted: restricted,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := user_model.CreateUser(u, overwriteDefault); err != nil {
|
||||||
return fmt.Errorf("CreateUser: %v", err)
|
return fmt.Errorf("CreateUser: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ func (o outputType) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var outputTypeEnum = &outputType{
|
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",
|
Default: "zip",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +160,12 @@ func runDump(ctx *cli.Context) error {
|
|||||||
fatal("Deleting default logger failed. Can not write to stdout: %v", err)
|
fatal("Deleting default logger failed. Can not write to stdout: %v", err)
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
fileName += "." + outType
|
||||||
}
|
}
|
||||||
setting.LoadFromExisting()
|
setting.LoadFromExisting()
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
reponame := os.Getenv(models.EnvRepoName)
|
reponame := os.Getenv(models.EnvRepoName)
|
||||||
userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||||
prID, _ := strconv.ParseInt(os.Getenv(models.EnvPRID), 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{
|
hookOptions := private.HookOptions{
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
@@ -194,7 +194,7 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
||||||
GitPushOptions: pushOptions(),
|
GitPushOptions: pushOptions(),
|
||||||
PullRequestID: prID,
|
PullRequestID: prID,
|
||||||
IsDeployKey: isDeployKey,
|
DeployKeyID: deployKeyID,
|
||||||
}
|
}
|
||||||
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
|
|||||||
11
cmd/serv.go
11
cmd/serv.go
@@ -243,7 +243,7 @@ func runServ(c *cli.Context) error {
|
|||||||
os.Setenv(models.EnvPusherID, strconv.FormatInt(results.UserID, 10))
|
os.Setenv(models.EnvPusherID, strconv.FormatInt(results.UserID, 10))
|
||||||
os.Setenv(models.EnvRepoID, strconv.FormatInt(results.RepoID, 10))
|
os.Setenv(models.EnvRepoID, strconv.FormatInt(results.RepoID, 10))
|
||||||
os.Setenv(models.EnvPRID, fmt.Sprintf("%d", 0))
|
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.EnvKeyID, fmt.Sprintf("%d", results.KeyID))
|
||||||
os.Setenv(models.EnvAppURL, setting.AppURL)
|
os.Setenv(models.EnvAppURL, setting.AppURL)
|
||||||
|
|
||||||
@@ -297,6 +297,15 @@ func runServ(c *cli.Context) error {
|
|||||||
gitcmd = exec.CommandContext(ctx, verb, repoPath)
|
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.Dir = setting.RepoRootPath
|
||||||
gitcmd.Stdout = os.Stdout
|
gitcmd.Stdout = os.Stdout
|
||||||
gitcmd.Stdin = os.Stdin
|
gitcmd.Stdin = os.Stdin
|
||||||
|
|||||||
@@ -1075,7 +1075,7 @@ PATH =
|
|||||||
;SEARCH_REPO_DESCRIPTION = true
|
;SEARCH_REPO_DESCRIPTION = true
|
||||||
;;
|
;;
|
||||||
;; Whether to enable a Service Worker to cache frontend assets
|
;; Whether to enable a Service Worker to cache frontend assets
|
||||||
;USE_SERVICE_WORKER = true
|
;USE_SERVICE_WORKER = false
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ params:
|
|||||||
description: Git with a cup of tea
|
description: Git with a cup of tea
|
||||||
author: The Gitea Authors
|
author: The Gitea Authors
|
||||||
website: https://docs.gitea.io
|
website: https://docs.gitea.io
|
||||||
version: 1.16.0
|
version: 1.16.4
|
||||||
minGoVersion: 1.16
|
minGoVersion: 1.16
|
||||||
goVersion: 1.17
|
goVersion: 1.18
|
||||||
minNodeVersion: 12.17
|
minNodeVersion: 12.17
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
|
|||||||
add it to this config.
|
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.
|
- `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.
|
- `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`)
|
### UI - Admin (`ui.admin`)
|
||||||
|
|
||||||
|
|||||||
@@ -185,8 +185,6 @@ Before committing, make sure the linters pass:
|
|||||||
make lint-frontend
|
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
|
### 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.
|
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.
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ Vous devriez avoir une instance fonctionnelle de Gitea. Pour accèder à l'inter
|
|||||||
|
|
||||||
## Named Volumes
|
## Named Volumes
|
||||||
|
|
||||||
Ce guide aboutira à une installation avec les données Gita et PostgreSQL stockées dans des volumes nommés. Cela permet une sauvegarde, une restauration et des mises à niveau en toute simplicité.
|
Ce guide aboutira à une installation avec les données Gitea et PostgreSQL stockées dans des volumes nommés. Cela permet une sauvegarde, une restauration et des mises à niveau en toute simplicité.
|
||||||
|
|
||||||
### The Database
|
### The Database
|
||||||
|
|
||||||
|
|||||||
152
go.mod
152
go.mod
@@ -3,7 +3,6 @@ module code.gitea.io/gitea
|
|||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.78.0 // indirect
|
|
||||||
code.gitea.io/gitea-vet v0.2.1
|
code.gitea.io/gitea-vet v0.2.1
|
||||||
code.gitea.io/sdk/gitea v0.15.1
|
code.gitea.io/sdk/gitea v0.15.1
|
||||||
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb
|
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/go-chi/session v0.0.0-20211218221615-e3605d8b28b8
|
||||||
gitea.com/lunny/levelqueue v0.4.1
|
gitea.com/lunny/levelqueue v0.4.1
|
||||||
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
|
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/NYTimes/gziphandler v1.1.1
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20210705153151-cc34b1f6908b // indirect
|
github.com/PuerkitoBio/goquery v1.8.0
|
||||||
github.com/PuerkitoBio/goquery v1.7.0
|
github.com/alecthomas/chroma v0.10.0
|
||||||
github.com/alecthomas/chroma v0.9.4
|
github.com/blevesearch/bleve/v2 v2.3.1
|
||||||
github.com/andybalholm/brotli v1.0.3 // indirect
|
github.com/caddyserver/certmagic v0.15.4
|
||||||
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/chi-middleware/proxy v1.1.1
|
github.com/chi-middleware/proxy v1.1.1
|
||||||
github.com/couchbase/go-couchbase v0.0.0-20210224140812-5740cd35f448 // indirect
|
github.com/denisenkom/go-mssqldb v0.12.0
|
||||||
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/djherbis/buffer v1.2.0
|
github.com/djherbis/buffer v1.2.0
|
||||||
github.com/djherbis/nio/v3 v3.0.1
|
github.com/djherbis/nio/v3 v3.0.1
|
||||||
github.com/duo-labs/webauthn v0.0.0-20220122034320-81aea484c951
|
github.com/duo-labs/webauthn v0.0.0-20220122034320-81aea484c951
|
||||||
@@ -36,8 +26,7 @@ require (
|
|||||||
github.com/emirpasic/gods v1.12.0
|
github.com/emirpasic/gods v1.12.0
|
||||||
github.com/ethantkoenig/rupture v1.0.0
|
github.com/ethantkoenig/rupture v1.0.0
|
||||||
github.com/gliderlabs/ssh v0.3.3
|
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.7
|
||||||
github.com/go-chi/chi/v5 v5.0.4
|
|
||||||
github.com/go-chi/cors v1.2.0
|
github.com/go-chi/cors v1.2.0
|
||||||
github.com/go-enry/go-enry/v2 v2.7.1
|
github.com/go-enry/go-enry/v2 v2.7.1
|
||||||
github.com/go-git/go-billy/v5 v5.3.1
|
github.com/go-git/go-billy/v5 v5.3.1
|
||||||
@@ -48,97 +37,124 @@ require (
|
|||||||
github.com/go-swagger/go-swagger v0.27.0
|
github.com/go-swagger/go-swagger v0.27.0
|
||||||
github.com/go-testfixtures/testfixtures/v3 v3.6.1
|
github.com/go-testfixtures/testfixtures/v3 v3.6.1
|
||||||
github.com/gobwas/glob v0.2.3
|
github.com/gobwas/glob v0.2.3
|
||||||
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28
|
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
||||||
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
|
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
|
||||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||||
github.com/golang-jwt/jwt/v4 v4.2.0
|
github.com/golang-jwt/jwt/v4 v4.3.0
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
|
||||||
github.com/google/go-github/v39 v39.2.0
|
github.com/google/go-github/v39 v39.2.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/gorilla/feeds v1.1.1
|
github.com/gorilla/feeds v1.1.1
|
||||||
github.com/gorilla/mux v1.8.0 // indirect
|
|
||||||
github.com/gorilla/sessions v1.2.1
|
github.com/gorilla/sessions v1.2.1
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-version v1.4.0
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
|
||||||
github.com/hashicorp/go-version v1.3.1
|
|
||||||
github.com/hashicorp/golang-lru v0.5.4
|
github.com/hashicorp/golang-lru v0.5.4
|
||||||
github.com/huandu/xstrings v1.3.2
|
github.com/huandu/xstrings v1.3.2
|
||||||
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7
|
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/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/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/cpuid/v2 v2.0.9
|
||||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
|
||||||
github.com/lib/pq v1.10.2
|
github.com/lib/pq v1.10.2
|
||||||
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
||||||
github.com/markbates/goth v1.68.0
|
github.com/markbates/goth v1.69.0
|
||||||
github.com/mattn/go-isatty v0.0.13
|
github.com/mattn/go-isatty v0.0.14
|
||||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
github.com/mattn/go-sqlite3 v1.14.12
|
||||||
github.com/mattn/go-sqlite3 v1.14.8
|
github.com/mholt/archiver/v3 v3.5.1
|
||||||
github.com/mholt/archiver/v3 v3.5.0
|
github.com/microcosm-cc/bluemonday v1.0.18
|
||||||
github.com/microcosm-cc/bluemonday v1.0.16
|
github.com/minio/minio-go/v7 v7.0.23
|
||||||
github.com/minio/md5-simd v1.1.2 // indirect
|
github.com/msteinert/pam v1.0.0
|
||||||
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/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||||
github.com/niklasfasching/go-org v1.6.1
|
github.com/niklasfasching/go-org v1.6.2
|
||||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
|
||||||
github.com/oliamb/cutter v0.2.2
|
github.com/oliamb/cutter v0.2.2
|
||||||
github.com/olivere/elastic/v7 v7.0.25
|
github.com/olivere/elastic/v7 v7.0.31
|
||||||
github.com/pelletier/go-toml v1.9.0 // indirect
|
|
||||||
github.com/pierrec/lz4/v4 v4.1.8 // indirect
|
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/pquerna/otp v1.3.0
|
github.com/pquerna/otp v1.3.0
|
||||||
github.com/prometheus/client_golang v1.11.0
|
github.com/prometheus/client_golang v1.12.1
|
||||||
github.com/quasoft/websspi v1.0.0
|
github.com/quasoft/websspi v1.1.2
|
||||||
github.com/rs/xid v1.3.0 // indirect
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
|
||||||
github.com/sergi/go-diff v1.2.0
|
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/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/stretchr/testify v1.7.0
|
||||||
github.com/syndtr/goleveldb v1.0.0
|
github.com/syndtr/goleveldb v1.0.0
|
||||||
github.com/tstranex/u2f 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/com v1.0.1
|
||||||
github.com/unknwon/i18n v0.0.0-20210321134014-0ebbf2df1c44
|
github.com/unknwon/i18n v0.0.0-20210321134014-0ebbf2df1c44
|
||||||
github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae
|
github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae
|
||||||
github.com/unrolled/render v1.4.0
|
github.com/unrolled/render v1.4.0
|
||||||
github.com/urfave/cli v1.22.5
|
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/yohcop/openid-go v1.0.0
|
||||||
github.com/yuin/goldmark v1.4.0
|
github.com/yuin/goldmark v1.4.11
|
||||||
github.com/yuin/goldmark-highlighting v0.0.0-20210516132338-9216f9c5aa01
|
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594
|
||||||
github.com/yuin/goldmark-meta v1.0.0
|
github.com/yuin/goldmark-meta v1.1.0
|
||||||
go.etcd.io/bbolt v1.3.6 // indirect
|
|
||||||
go.jolheiser.com/hcaptcha v0.0.4
|
go.jolheiser.com/hcaptcha v0.0.4
|
||||||
go.jolheiser.com/pwn v0.0.3
|
go.jolheiser.com/pwn v0.0.3
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
go.uber.org/multierr v1.7.0 // indirect
|
go.uber.org/multierr v1.8.0 // indirect
|
||||||
go.uber.org/zap v1.19.0 // indirect
|
go.uber.org/zap v1.21.0 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
|
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
|
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e
|
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9
|
||||||
golang.org/x/text v0.3.7
|
golang.org/x/text v0.3.7
|
||||||
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect
|
golang.org/x/tools v0.1.9
|
||||||
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
|
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
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
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
mvdan.cc/xurls/v2 v2.2.0
|
mvdan.cc/xurls/v2 v2.2.0
|
||||||
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
|
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
|
||||||
xorm.io/builder v0.3.9
|
xorm.io/builder v0.3.10
|
||||||
xorm.io/xorm v1.2.5
|
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/hashicorp/go-version => github.com/6543/go-version v1.3.1
|
||||||
|
|
||||||
replace github.com/markbates/goth v1.68.0 => github.com/zeripath/goth v1.68.1-0.20220109111530-754359885dce
|
replace github.com/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", "")
|
results, err := private.ServCommand(ctx, 1, "user2", "repo1", perm.AccessModeWrite, "git-upload-pack", "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.False(t, results.IsWiki)
|
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, int64(1), results.KeyID)
|
||||||
assert.Equal(t, "user2@localhost", results.KeyName)
|
assert.Equal(t, "user2@localhost", results.KeyName)
|
||||||
assert.Equal(t, "user2", results.UserName)
|
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", "")
|
results, err = private.ServCommand(ctx, 1, "user15", "big_test_public_1", perm.AccessModeRead, "git-upload-pack", "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.False(t, results.IsWiki)
|
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, int64(1), results.KeyID)
|
||||||
assert.Equal(t, "user2@localhost", results.KeyName)
|
assert.Equal(t, "user2@localhost", results.KeyName)
|
||||||
assert.Equal(t, "user2", results.UserName)
|
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", "")
|
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_1", perm.AccessModeRead, "git-upload-pack", "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.False(t, results.IsWiki)
|
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, deployKey.KeyID, results.KeyID)
|
||||||
assert.Equal(t, "test-deploy", results.KeyName)
|
assert.Equal(t, "test-deploy", results.KeyName)
|
||||||
assert.Equal(t, "user15", results.UserName)
|
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", "")
|
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_2", perm.AccessModeRead, "git-upload-pack", "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.False(t, results.IsWiki)
|
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, deployKey.KeyID, results.KeyID)
|
||||||
assert.Equal(t, "test-deploy", results.KeyName)
|
assert.Equal(t, "test-deploy", results.KeyName)
|
||||||
assert.Equal(t, "user15", results.UserName)
|
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", "")
|
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_2", perm.AccessModeWrite, "git-upload-pack", "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.False(t, results.IsWiki)
|
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, deployKey.KeyID, results.KeyID)
|
||||||
assert.Equal(t, "test-deploy", results.KeyName)
|
assert.Equal(t, "test-deploy", results.KeyName)
|
||||||
assert.Equal(t, "user15", results.UserName)
|
assert.Equal(t, "user15", results.UserName)
|
||||||
|
|||||||
@@ -69,6 +69,12 @@ func TestAPIAddEmail(t *testing.T) {
|
|||||||
Primary: false,
|
Primary: false,
|
||||||
},
|
},
|
||||||
}, emails)
|
}, 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) {
|
func TestAPIDeleteEmail(t *testing.T) {
|
||||||
|
|||||||
@@ -112,6 +112,13 @@ func TestMain(m *testing.M) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
os.Unsetenv("GIT_AUTHOR_NAME")
|
||||||
|
os.Unsetenv("GIT_AUTHOR_EMAIL")
|
||||||
|
os.Unsetenv("GIT_AUTHOR_DATE")
|
||||||
|
os.Unsetenv("GIT_COMMITTER_NAME")
|
||||||
|
os.Unsetenv("GIT_COMMITTER_EMAIL")
|
||||||
|
os.Unsetenv("GIT_COMMITTER_DATE")
|
||||||
|
|
||||||
err := unittest.InitFixtures(
|
err := unittest.InitFixtures(
|
||||||
unittest.FixturesOptions{
|
unittest.FixturesOptions{
|
||||||
Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"),
|
Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"),
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import (
|
|||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/test"
|
"code.gitea.io/gitea/modules/test"
|
||||||
"code.gitea.io/gitea/services/pull"
|
"code.gitea.io/gitea/services/pull"
|
||||||
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
|
files_service "code.gitea.io/gitea/services/repository/files"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/unknwon/i18n"
|
"github.com/unknwon/i18n"
|
||||||
@@ -65,7 +67,7 @@ func testPullCleanUp(t *testing.T, session *TestSession, user, repo, pullnum str
|
|||||||
|
|
||||||
func TestPullMerge(t *testing.T) {
|
func TestPullMerge(t *testing.T) {
|
||||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number
|
hookTasks, err := webhook.HookTasks(1, 1) // Retrieve previous hook number
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
hookTasksLenBefore := len(hookTasks)
|
hookTasksLenBefore := len(hookTasks)
|
||||||
|
|
||||||
@@ -87,7 +89,7 @@ func TestPullMerge(t *testing.T) {
|
|||||||
|
|
||||||
func TestPullRebase(t *testing.T) {
|
func TestPullRebase(t *testing.T) {
|
||||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number
|
hookTasks, err := webhook.HookTasks(1, 1) // Retrieve previous hook number
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
hookTasksLenBefore := len(hookTasks)
|
hookTasksLenBefore := len(hookTasks)
|
||||||
|
|
||||||
@@ -109,7 +111,7 @@ func TestPullRebase(t *testing.T) {
|
|||||||
|
|
||||||
func TestPullRebaseMerge(t *testing.T) {
|
func TestPullRebaseMerge(t *testing.T) {
|
||||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number
|
hookTasks, err := webhook.HookTasks(1, 1) // Retrieve previous hook number
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
hookTasksLenBefore := len(hookTasks)
|
hookTasksLenBefore := len(hookTasks)
|
||||||
|
|
||||||
@@ -131,7 +133,7 @@ func TestPullRebaseMerge(t *testing.T) {
|
|||||||
|
|
||||||
func TestPullSquash(t *testing.T) {
|
func TestPullSquash(t *testing.T) {
|
||||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number
|
hookTasks, err := webhook.HookTasks(1, 1) // Retrieve previous hook number
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
hookTasksLenBefore := len(hookTasks)
|
hookTasksLenBefore := len(hookTasks)
|
||||||
|
|
||||||
@@ -335,3 +337,74 @@ func TestCantMergeUnrelated(t *testing.T) {
|
|||||||
gitRepo.Close()
|
gitRepo.Close()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConflictChecking(t *testing.T) {
|
||||||
|
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||||
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
|
||||||
|
|
||||||
|
// Create new clean repo to test conflict checking.
|
||||||
|
baseRepo, err := repo_service.CreateRepository(user, user, models.CreateRepoOptions{
|
||||||
|
Name: "conflict-checking",
|
||||||
|
Description: "Tempo repo",
|
||||||
|
AutoInit: true,
|
||||||
|
Readme: "Default",
|
||||||
|
DefaultBranch: "main",
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, baseRepo)
|
||||||
|
|
||||||
|
// create a commit on new branch.
|
||||||
|
_, err = files_service.CreateOrUpdateRepoFile(baseRepo, user, &files_service.UpdateRepoFileOptions{
|
||||||
|
TreePath: "important_file",
|
||||||
|
Message: "Add a important file",
|
||||||
|
Content: "Just a non-important file",
|
||||||
|
IsNewFile: true,
|
||||||
|
OldBranch: "main",
|
||||||
|
NewBranch: "important-secrets",
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// create a commit on main branch.
|
||||||
|
_, err = files_service.CreateOrUpdateRepoFile(baseRepo, user, &files_service.UpdateRepoFileOptions{
|
||||||
|
TreePath: "important_file",
|
||||||
|
Message: "Add a important file",
|
||||||
|
Content: "Not the same content :P",
|
||||||
|
IsNewFile: true,
|
||||||
|
OldBranch: "main",
|
||||||
|
NewBranch: "main",
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// create Pull to merge the important-secrets branch into main branch.
|
||||||
|
pullIssue := &models.Issue{
|
||||||
|
RepoID: baseRepo.ID,
|
||||||
|
Title: "PR with conflict!",
|
||||||
|
PosterID: user.ID,
|
||||||
|
Poster: user,
|
||||||
|
IsPull: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
pullRequest := &models.PullRequest{
|
||||||
|
HeadRepoID: baseRepo.ID,
|
||||||
|
BaseRepoID: baseRepo.ID,
|
||||||
|
HeadBranch: "important-secrets",
|
||||||
|
BaseBranch: "main",
|
||||||
|
HeadRepo: baseRepo,
|
||||||
|
BaseRepo: baseRepo,
|
||||||
|
Type: models.PullRequestGitea,
|
||||||
|
}
|
||||||
|
err = pull.NewPullRequest(baseRepo, pullIssue, nil, nil, pullRequest, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{Title: "PR with conflict!"}).(*models.Issue)
|
||||||
|
conflictingPR, err := models.GetPullRequestByIssueID(issue.ID)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Ensure conflictedFiles is populated.
|
||||||
|
assert.Equal(t, 1, len(conflictingPR.ConflictedFiles))
|
||||||
|
// Check if status is correct.
|
||||||
|
assert.Equal(t, models.PullRequestStatusConflict, conflictingPR.Status)
|
||||||
|
// Ensure that mergeable returns false
|
||||||
|
assert.False(t, conflictingPR.Mergeable())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -337,7 +337,7 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
|
|||||||
|
|
||||||
actions := make([]*Action, 0, setting.UI.FeedPagingNum)
|
actions := make([]*Action, 0, setting.UI.FeedPagingNum)
|
||||||
|
|
||||||
if err := db.GetEngine(db.DefaultContext).Limit(setting.UI.FeedPagingNum).Desc("created_unix").Where(cond).Find(&actions); err != nil {
|
if err := db.GetEngine(db.DefaultContext).Limit(setting.UI.FeedPagingNum).Desc("`action`.created_unix").Where(cond).Join("INNER", "repository", "`repository`.id = `action`.repo_id").Find(&actions); err != nil {
|
||||||
return nil, fmt.Errorf("Find: %v", err)
|
return nil, fmt.Errorf("Find: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,7 +401,7 @@ func activityQueryCondition(opts GetFeedsOptions) (builder.Cond, error) {
|
|||||||
cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID})
|
cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID})
|
||||||
}
|
}
|
||||||
if !opts.IncludePrivate {
|
if !opts.IncludePrivate {
|
||||||
cond = cond.And(builder.Eq{"is_private": false})
|
cond = cond.And(builder.Eq{"`action`.is_private": false})
|
||||||
}
|
}
|
||||||
if !opts.IncludeDeleted {
|
if !opts.IncludeDeleted {
|
||||||
cond = cond.And(builder.Eq{"is_deleted": false})
|
cond = cond.And(builder.Eq{"is_deleted": false})
|
||||||
@@ -414,8 +414,8 @@ func activityQueryCondition(opts GetFeedsOptions) (builder.Cond, error) {
|
|||||||
} else {
|
} else {
|
||||||
dateHigh := dateLow.Add(86399000000000) // 23h59m59s
|
dateHigh := dateLow.Add(86399000000000) // 23h59m59s
|
||||||
|
|
||||||
cond = cond.And(builder.Gte{"created_unix": dateLow.Unix()})
|
cond = cond.And(builder.Gte{"`action`.created_unix": dateLow.Unix()})
|
||||||
cond = cond.And(builder.Lte{"created_unix": dateHigh.Unix()})
|
cond = cond.And(builder.Lte{"`action`.created_unix": dateHigh.Unix()})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -129,3 +129,20 @@ func TestNotifyWatchers(t *testing.T) {
|
|||||||
OpType: action.OpType,
|
OpType: action.OpType,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetFeedsCorrupted(t *testing.T) {
|
||||||
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
|
||||||
|
unittest.AssertExistsAndLoadBean(t, &Action{
|
||||||
|
ID: 8,
|
||||||
|
RepoID: 1700,
|
||||||
|
})
|
||||||
|
|
||||||
|
actions, err := GetFeeds(GetFeedsOptions{
|
||||||
|
RequestedUser: user,
|
||||||
|
Actor: user,
|
||||||
|
IncludePrivate: true,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, actions, 0)
|
||||||
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ func (key *DeployKey) GetContent() error {
|
|||||||
return nil
|
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 {
|
func (key *DeployKey) IsReadOnly() bool {
|
||||||
return key.Mode == perm.AccessModeRead
|
return key.Mode == perm.AccessModeRead
|
||||||
}
|
}
|
||||||
@@ -203,12 +203,6 @@ func UpdateDeployKeyCols(key *DeployKey, cols ...string) error {
|
|||||||
return err
|
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
|
// ListDeployKeysOptions are options for ListDeployKeys
|
||||||
type ListDeployKeysOptions struct {
|
type ListDeployKeysOptions struct {
|
||||||
db.ListOptions
|
db.ListOptions
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
@@ -247,3 +248,23 @@ func FixIssueLabelWithOutsideLabels() (int64, error) {
|
|||||||
|
|
||||||
return res.RowsAffected()
|
return res.RowsAffected()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CountActionCreatedUnixString count actions where created_unix is an empty string
|
||||||
|
func CountActionCreatedUnixString() (int64, error) {
|
||||||
|
if setting.Database.UseSQLite3 {
|
||||||
|
return db.GetEngine(db.DefaultContext).Where(`created_unix = ""`).Count(new(Action))
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FixActionCreatedUnixString set created_unix to zero if it is an empty string
|
||||||
|
func FixActionCreatedUnixString() (int64, error) {
|
||||||
|
if setting.Database.UseSQLite3 {
|
||||||
|
res, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = 0 WHERE created_unix = ""`)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return res.RowsAffected()
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@@ -33,3 +34,46 @@ func TestDeleteOrphanedObjects(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, countBefore, countAfter)
|
assert.EqualValues(t, countBefore, countAfter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConsistencyUpdateAction(t *testing.T) {
|
||||||
|
if !setting.Database.UseSQLite3 {
|
||||||
|
t.Skip("Test is only for SQLite database.")
|
||||||
|
}
|
||||||
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
id := 8
|
||||||
|
unittest.AssertExistsAndLoadBean(t, &Action{
|
||||||
|
ID: int64(id),
|
||||||
|
})
|
||||||
|
_, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = "" WHERE id = ?`, id)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
actions := make([]*Action, 0, 1)
|
||||||
|
//
|
||||||
|
// XORM returns an error when created_unix is a string
|
||||||
|
//
|
||||||
|
err = db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions)
|
||||||
|
if assert.Error(t, err) {
|
||||||
|
assert.Contains(t, err.Error(), "type string to a int64: invalid syntax")
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// Get rid of incorrectly set created_unix
|
||||||
|
//
|
||||||
|
count, err := CountActionCreatedUnixString()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 1, count)
|
||||||
|
count, err = FixActionCreatedUnixString()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 1, count)
|
||||||
|
|
||||||
|
count, err = CountActionCreatedUnixString()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 0, count)
|
||||||
|
count, err = FixActionCreatedUnixString()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 0, count)
|
||||||
|
|
||||||
|
//
|
||||||
|
// XORM must be happy now
|
||||||
|
//
|
||||||
|
assert.NoError(t, db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions))
|
||||||
|
unittest.CheckConsistencyFor(t, &Action{})
|
||||||
|
}
|
||||||
|
|||||||
@@ -332,7 +332,6 @@ type ErrInvalidCloneAddr struct {
|
|||||||
IsProtocolInvalid bool
|
IsProtocolInvalid bool
|
||||||
IsPermissionDenied bool
|
IsPermissionDenied bool
|
||||||
LocalPath bool
|
LocalPath bool
|
||||||
NotResolvedIP bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsErrInvalidCloneAddr checks if an error is a ErrInvalidCloneAddr.
|
// IsErrInvalidCloneAddr checks if an error is a ErrInvalidCloneAddr.
|
||||||
@@ -342,9 +341,6 @@ func IsErrInvalidCloneAddr(err error) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (err *ErrInvalidCloneAddr) Error() string {
|
func (err *ErrInvalidCloneAddr) Error() string {
|
||||||
if err.NotResolvedIP {
|
|
||||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed: unknown hostname", err.Host)
|
|
||||||
}
|
|
||||||
if err.IsInvalidPath {
|
if err.IsInvalidPath {
|
||||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided path is invalid", err.Host)
|
return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided path is invalid", err.Host)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,3 +56,11 @@
|
|||||||
repo_id: 8
|
repo_id: 8
|
||||||
is_private: false
|
is_private: false
|
||||||
created_unix: 1603011540 # grouped with id:7
|
created_unix: 1603011540 # grouped with id:7
|
||||||
|
|
||||||
|
- id: 8
|
||||||
|
user_id: 1
|
||||||
|
op_type: 12 # close issue
|
||||||
|
act_user_id: 1
|
||||||
|
repo_id: 1700 # dangling intentional
|
||||||
|
is_private: false
|
||||||
|
created_unix: 1603011541
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ const (
|
|||||||
EnvPusherName = "GITEA_PUSHER_NAME"
|
EnvPusherName = "GITEA_PUSHER_NAME"
|
||||||
EnvPusherEmail = "GITEA_PUSHER_EMAIL"
|
EnvPusherEmail = "GITEA_PUSHER_EMAIL"
|
||||||
EnvPusherID = "GITEA_PUSHER_ID"
|
EnvPusherID = "GITEA_PUSHER_ID"
|
||||||
EnvKeyID = "GITEA_KEY_ID"
|
EnvKeyID = "GITEA_KEY_ID" // public key ID
|
||||||
EnvIsDeployKey = "GITEA_IS_DEPLOY_KEY"
|
EnvDeployKeyID = "GITEA_DEPLOY_KEY_ID"
|
||||||
EnvPRID = "GITEA_PR_ID"
|
EnvPRID = "GITEA_PR_ID"
|
||||||
EnvIsInternal = "GITEA_INTERNAL_PUSH"
|
EnvIsInternal = "GITEA_INTERNAL_PUSH"
|
||||||
EnvAppURL = "GITEA_ROOT_URL"
|
EnvAppURL = "GITEA_ROOT_URL"
|
||||||
|
|||||||
@@ -1165,7 +1165,8 @@ func GetIssuesByIDs(issueIDs []int64) ([]*Issue, error) {
|
|||||||
// IssuesOptions represents options of an issue.
|
// IssuesOptions represents options of an issue.
|
||||||
type IssuesOptions struct {
|
type IssuesOptions struct {
|
||||||
db.ListOptions
|
db.ListOptions
|
||||||
RepoIDs []int64 // include all repos if empty
|
RepoID int64 // overwrites RepoCond if not 0
|
||||||
|
RepoCond builder.Cond
|
||||||
AssigneeID int64
|
AssigneeID int64
|
||||||
PosterID int64
|
PosterID int64
|
||||||
MentionedID int64
|
MentionedID int64
|
||||||
@@ -1238,7 +1239,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 {
|
if opts.Page >= 0 && opts.PageSize > 0 {
|
||||||
var start int
|
var start int
|
||||||
if opts.Page == 0 {
|
if opts.Page == 0 {
|
||||||
@@ -1248,20 +1249,23 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) {
|
|||||||
}
|
}
|
||||||
sess.Limit(opts.PageSize, start)
|
sess.Limit(opts.PageSize, start)
|
||||||
}
|
}
|
||||||
|
opts.setupSessionNoLimit(sess)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
|
||||||
if len(opts.IssueIDs) > 0 {
|
if len(opts.IssueIDs) > 0 {
|
||||||
sess.In("issue.id", opts.IssueIDs)
|
sess.In("issue.id", opts.IssueIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(opts.RepoIDs) > 0 {
|
if opts.RepoID != 0 {
|
||||||
applyReposCondition(sess, opts.RepoIDs)
|
opts.RepoCond = builder.Eq{"issue.repo_id": opts.RepoID}
|
||||||
|
}
|
||||||
|
if opts.RepoCond != nil {
|
||||||
|
sess.And(opts.RepoCond)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch opts.IsClosed {
|
if !opts.IsClosed.IsNone() {
|
||||||
case util.OptionalBoolTrue:
|
sess.And("issue.is_closed=?", opts.IsClosed.IsTrue())
|
||||||
sess.And("issue.is_closed=?", true)
|
|
||||||
case util.OptionalBoolFalse:
|
|
||||||
sess.And("issue.is_closed=?", false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.AssigneeID > 0 {
|
if opts.AssigneeID > 0 {
|
||||||
@@ -1342,9 +1346,7 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if opts.User != nil {
|
if opts.User != nil {
|
||||||
sess.And(
|
sess.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.IsTrue()))
|
||||||
issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.IsTrue()),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1380,10 +1382,6 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *Organizati
|
|||||||
return cond
|
return cond
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyReposCondition(sess *xorm.Session, repoIDs []int64) *xorm.Session {
|
|
||||||
return sess.In("issue.repo_id", repoIDs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func applyAssigneeCondition(sess *xorm.Session, assigneeID int64) *xorm.Session {
|
func applyAssigneeCondition(sess *xorm.Session, assigneeID int64) *xorm.Session {
|
||||||
return sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
|
return sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
|
||||||
And("issue_assignees.assignee_id = ?", assigneeID)
|
And("issue_assignees.assignee_id = ?", assigneeID)
|
||||||
@@ -1413,8 +1411,7 @@ func CountIssuesByRepo(opts *IssuesOptions) (map[int64]int64, error) {
|
|||||||
e := db.GetEngine(db.DefaultContext)
|
e := db.GetEngine(db.DefaultContext)
|
||||||
|
|
||||||
sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
||||||
|
opts.setupSessionNoLimit(sess)
|
||||||
opts.setupSession(sess)
|
|
||||||
|
|
||||||
countsSlice := make([]*struct {
|
countsSlice := make([]*struct {
|
||||||
RepoID int64
|
RepoID int64
|
||||||
@@ -1424,7 +1421,7 @@ func CountIssuesByRepo(opts *IssuesOptions) (map[int64]int64, error) {
|
|||||||
Select("issue.repo_id AS repo_id, COUNT(*) AS count").
|
Select("issue.repo_id AS repo_id, COUNT(*) AS count").
|
||||||
Table("issue").
|
Table("issue").
|
||||||
Find(&countsSlice); err != nil {
|
Find(&countsSlice); err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("unable to CountIssuesByRepo: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
countMap := make(map[int64]int64, len(countsSlice))
|
countMap := make(map[int64]int64, len(countsSlice))
|
||||||
@@ -1440,15 +1437,14 @@ func GetRepoIDsForIssuesOptions(opts *IssuesOptions, user *user_model.User) ([]i
|
|||||||
e := db.GetEngine(db.DefaultContext)
|
e := db.GetEngine(db.DefaultContext)
|
||||||
|
|
||||||
sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
||||||
|
opts.setupSessionNoLimit(sess)
|
||||||
opts.setupSession(sess)
|
|
||||||
|
|
||||||
accessCond := accessibleRepositoryCondition(user)
|
accessCond := accessibleRepositoryCondition(user)
|
||||||
if err := sess.Where(accessCond).
|
if err := sess.Where(accessCond).
|
||||||
Distinct("issue.repo_id").
|
Distinct("issue.repo_id").
|
||||||
Table("issue").
|
Table("issue").
|
||||||
Find(&repoIDs); err != nil {
|
Find(&repoIDs); err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("unable to GetRepoIDsForIssuesOptions: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return repoIDs, nil
|
return repoIDs, nil
|
||||||
@@ -1459,17 +1455,16 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
|
|||||||
e := db.GetEngine(db.DefaultContext)
|
e := db.GetEngine(db.DefaultContext)
|
||||||
|
|
||||||
sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
||||||
opts.setupSession(sess)
|
opts.setupSessionWithLimit(sess)
|
||||||
sortIssuesSession(sess, opts.SortType, opts.PriorityRepoID)
|
sortIssuesSession(sess, opts.SortType, opts.PriorityRepoID)
|
||||||
|
|
||||||
issues := make([]*Issue, 0, opts.ListOptions.PageSize)
|
issues := make([]*Issue, 0, opts.ListOptions.PageSize)
|
||||||
if err := sess.Find(&issues); err != nil {
|
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 {
|
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
|
return issues, nil
|
||||||
@@ -1480,18 +1475,18 @@ func CountIssues(opts *IssuesOptions) (int64, error) {
|
|||||||
e := db.GetEngine(db.DefaultContext)
|
e := db.GetEngine(db.DefaultContext)
|
||||||
|
|
||||||
countsSlice := make([]*struct {
|
countsSlice := make([]*struct {
|
||||||
RepoID int64
|
Count int64
|
||||||
Count int64
|
|
||||||
}, 0, 1)
|
}, 0, 1)
|
||||||
|
|
||||||
sess := e.Select("COUNT(issue.id) AS count").Table("issue")
|
sess := e.Select("COUNT(issue.id) AS count").Table("issue")
|
||||||
sess.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
sess.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
||||||
opts.setupSession(sess)
|
opts.setupSessionNoLimit(sess)
|
||||||
|
|
||||||
if err := sess.Find(&countsSlice); err != nil {
|
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 {
|
if len(countsSlice) != 1 {
|
||||||
return 0, fmt.Errorf("there is less than one result sql record")
|
return 0, fmt.Errorf("unable to get one row result when CountIssues, row count=%d", len(countsSlice))
|
||||||
}
|
}
|
||||||
return countsSlice[0].Count, nil
|
return countsSlice[0].Count, nil
|
||||||
}
|
}
|
||||||
@@ -1551,6 +1546,7 @@ const (
|
|||||||
FilterModeCreate
|
FilterModeCreate
|
||||||
FilterModeMention
|
FilterModeMention
|
||||||
FilterModeReviewRequested
|
FilterModeReviewRequested
|
||||||
|
FilterModeYourRepositories
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseCountResult(results []map[string][]byte) int64 {
|
func parseCountResult(results []map[string][]byte) int64 {
|
||||||
@@ -1695,6 +1691,7 @@ type UserIssueStatsOptions struct {
|
|||||||
IssueIDs []int64
|
IssueIDs []int64
|
||||||
IsArchived util.OptionalBool
|
IsArchived util.OptionalBool
|
||||||
LabelIDs []int64
|
LabelIDs []int64
|
||||||
|
RepoCond builder.Cond
|
||||||
Org *Organization
|
Org *Organization
|
||||||
Team *Team
|
Team *Team
|
||||||
}
|
}
|
||||||
@@ -1712,6 +1709,9 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
|
|||||||
if len(opts.IssueIDs) > 0 {
|
if len(opts.IssueIDs) > 0 {
|
||||||
cond = cond.And(builder.In("issue.id", opts.IssueIDs))
|
cond = cond.And(builder.In("issue.id", opts.IssueIDs))
|
||||||
}
|
}
|
||||||
|
if opts.RepoCond != nil {
|
||||||
|
cond = cond.And(opts.RepoCond)
|
||||||
|
}
|
||||||
|
|
||||||
if opts.UserID > 0 {
|
if opts.UserID > 0 {
|
||||||
cond = cond.And(issuePullAccessibleRepoCond("issue.repo_id", opts.UserID, opts.Org, opts.Team, opts.IsPull))
|
cond = cond.And(issuePullAccessibleRepoCond("issue.repo_id", opts.UserID, opts.Org, opts.Team, opts.IsPull))
|
||||||
@@ -1733,7 +1733,7 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch opts.FilterMode {
|
switch opts.FilterMode {
|
||||||
case FilterModeAll:
|
case FilterModeAll, FilterModeYourRepositories:
|
||||||
stats.OpenCount, err = sess(cond).
|
stats.OpenCount, err = sess(cond).
|
||||||
And("issue.is_closed = ?", false).
|
And("issue.is_closed = ?", false).
|
||||||
Count(new(Issue))
|
Count(new(Issue))
|
||||||
|
|||||||
@@ -101,12 +101,9 @@ func (label *Label) CalOpenIssues() {
|
|||||||
|
|
||||||
// CalOpenOrgIssues calculates the open issues of a label for a specific repo
|
// CalOpenOrgIssues calculates the open issues of a label for a specific repo
|
||||||
func (label *Label) CalOpenOrgIssues(repoID, labelID int64) {
|
func (label *Label) CalOpenOrgIssues(repoID, labelID int64) {
|
||||||
repoIDs := []int64{repoID}
|
|
||||||
labelIDs := []int64{labelID}
|
|
||||||
|
|
||||||
counts, _ := CountIssuesByRepo(&IssuesOptions{
|
counts, _ := CountIssuesByRepo(&IssuesOptions{
|
||||||
RepoIDs: repoIDs,
|
RepoID: repoID,
|
||||||
LabelIDs: labelIDs,
|
LabelIDs: []int64{labelID},
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, count := range counts {
|
for _, count := range counts {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIssue_ReplaceLabels(t *testing.T) {
|
func TestIssue_ReplaceLabels(t *testing.T) {
|
||||||
@@ -153,7 +154,7 @@ func TestIssues(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
IssuesOptions{
|
IssuesOptions{
|
||||||
RepoIDs: []int64{1, 3},
|
RepoCond: builder.In("repo_id", 1, 3),
|
||||||
SortType: "oldest",
|
SortType: "oldest",
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: db.ListOptions{
|
||||||
Page: 1,
|
Page: 1,
|
||||||
@@ -340,7 +341,7 @@ func TestGetRepoIDsForIssuesOptions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
IssuesOptions{
|
IssuesOptions{
|
||||||
RepoIDs: []int64{1, 2},
|
RepoCond: builder.In("repo_id", 1, 2),
|
||||||
},
|
},
|
||||||
[]int64{1, 2},
|
[]int64{1, 2},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -453,7 +453,7 @@ Please try upgrading to a lower version first (suggested v1.6.4), then upgrade t
|
|||||||
|
|
||||||
// Downgrading Gitea's database version not supported
|
// Downgrading Gitea's database version not supported
|
||||||
if int(v-minDBVersion) > len(migrations) {
|
if int(v-minDBVersion) > len(migrations) {
|
||||||
msg := fmt.Sprintf("Your database (migration version: %d) is for a newer Gita, you can not use the newer database for this old Gitea release (%d).", v, minDBVersion+len(migrations))
|
msg := fmt.Sprintf("Your database (migration version: %d) is for a newer Gitea, you can not use the newer database for this old Gitea release (%d).", v, minDBVersion+len(migrations))
|
||||||
msg += "\nGitea will exit to keep your database safe and unchanged. Please use the correct Gitea release, do not change the migration version manually (incorrect manual operation may lose data)."
|
msg += "\nGitea will exit to keep your database safe and unchanged. Please use the correct Gitea release, do not change the migration version manually (incorrect manual operation may lose data)."
|
||||||
if !setting.IsProd {
|
if !setting.IsProd {
|
||||||
msg += fmt.Sprintf("\nIf you are in development and really know what you're doing, you can force changing the migration version by executing: UPDATE version SET version=%d WHERE id=1;", minDBVersion+len(migrations))
|
msg += fmt.Sprintf("\nIf you are in development and really know what you're doing, you can force changing the migration version by executing: UPDATE version SET version=%d WHERE id=1;", minDBVersion+len(migrations))
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
@@ -776,8 +777,45 @@ func DeleteTeam(t *Team) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := t.removeAllRepositories(ctx); err != nil {
|
// update branch protections
|
||||||
return err
|
{
|
||||||
|
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.
|
// Delete team-user.
|
||||||
@@ -902,11 +940,6 @@ func AddTeamMember(team *Team, userID int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get team and its repositories.
|
|
||||||
if err := team.GetRepositories(&SearchOrgTeamOptions{}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, committer, err := db.TxContext()
|
ctx, committer, err := db.TxContext()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -928,17 +961,51 @@ func AddTeamMember(team *Team, userID int64) error {
|
|||||||
team.NumMembers++
|
team.NumMembers++
|
||||||
|
|
||||||
// Give access to team repositories.
|
// Give access to team repositories.
|
||||||
for _, repo := range team.Repos {
|
// update exist access if mode become bigger
|
||||||
if err := recalculateUserAccess(ctx, repo, userID); err != nil {
|
subQuery := builder.Select("repo_id").From("team_repo").
|
||||||
return err
|
Where(builder.Eq{"team_id": team.ID})
|
||||||
}
|
|
||||||
if setting.Service.AutoWatchNewRepos {
|
if _, err := sess.Where("user_id=?", userID).
|
||||||
if err = repo_model.WatchRepoCtx(ctx, userID, repo.ID, true); err != nil {
|
In("repo_id", subQuery).
|
||||||
return err
|
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()
|
return committer.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -222,22 +222,19 @@ func (pr *PullRequest) loadProtectedBranch(ctx context.Context) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetDefaultMergeMessage returns default message used when merging pull request
|
// GetDefaultMergeMessage returns default message used when merging pull request
|
||||||
func (pr *PullRequest) GetDefaultMergeMessage() string {
|
func (pr *PullRequest) GetDefaultMergeMessage() (string, error) {
|
||||||
if pr.HeadRepo == nil {
|
if pr.HeadRepo == nil {
|
||||||
var err error
|
var err error
|
||||||
pr.HeadRepo, err = repo_model.GetRepositoryByID(pr.HeadRepoID)
|
pr.HeadRepo, err = repo_model.GetRepositoryByID(pr.HeadRepoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetRepositoryById[%d]: %v", pr.HeadRepoID, err)
|
return "", fmt.Errorf("GetRepositoryById[%d]: %v", pr.HeadRepoID, err)
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := pr.LoadIssue(); err != nil {
|
if err := pr.LoadIssue(); err != nil {
|
||||||
log.Error("Cannot load issue %d for PR id %d: Error: %v", pr.IssueID, pr.ID, err)
|
return "", fmt.Errorf("Cannot load issue %d for PR id %d: Error: %v", pr.IssueID, pr.ID, err)
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
if err := pr.LoadBaseRepo(); err != nil {
|
if err := pr.LoadBaseRepo(); err != nil {
|
||||||
log.Error("LoadBaseRepo: %v", err)
|
return "", fmt.Errorf("LoadBaseRepo: %v", err)
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
issueReference := "#"
|
issueReference := "#"
|
||||||
@@ -246,10 +243,10 @@ func (pr *PullRequest) GetDefaultMergeMessage() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if pr.BaseRepoID == pr.HeadRepoID {
|
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
|
// 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
|
// 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 {
|
if err := pr.LoadIssue(); err != nil {
|
||||||
log.Error("LoadIssue: %v", err)
|
return "", fmt.Errorf("LoadIssue: %v", err)
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
if err := pr.LoadBaseRepo(); err != nil {
|
if err := pr.LoadBaseRepo(); err != nil {
|
||||||
log.Error("LoadBaseRepo: %v", err)
|
return "", fmt.Errorf("LoadBaseRepo: %v", err)
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
if pr.BaseRepo.UnitEnabled(unit.TypeExternalTracker) {
|
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
|
// GetGitRefName returns git ref for hidden pull request branch
|
||||||
@@ -702,3 +697,14 @@ func (pr *PullRequest) GetHeadBranchHTMLURL() string {
|
|||||||
}
|
}
|
||||||
return pr.HeadRepo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(pr.HeadBranch)
|
return pr.HeadRepo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(pr.HeadBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mergeable returns if the pullrequest is mergeable.
|
||||||
|
func (pr *PullRequest) Mergeable() bool {
|
||||||
|
// If a pull request isn't mergable if it's:
|
||||||
|
// - Being conflict checked.
|
||||||
|
// - Has a conflict.
|
||||||
|
// - Received a error while being conflict checked.
|
||||||
|
// - Is a work-in-progress pull request.
|
||||||
|
return pr.Status != PullRequestStatusChecking && pr.Status != PullRequestStatusConflict &&
|
||||||
|
pr.Status != PullRequestStatusError && !pr.IsWorkInProgress()
|
||||||
|
}
|
||||||
|
|||||||
@@ -261,11 +261,15 @@ func TestPullRequest_GetDefaultMergeMessage_InternalTracker(t *testing.T) {
|
|||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2}).(*PullRequest)
|
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.BaseRepoID = 1
|
||||||
pr.HeadRepoID = 2
|
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) {
|
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)
|
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.BaseRepoID = 1
|
||||||
pr.HeadRepoID = 2
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ func CheckRepoUnitUser(repo *repo_model.Repository, user *user_model.User, unitT
|
|||||||
}
|
}
|
||||||
|
|
||||||
func checkRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *user_model.User, unitType unit.Type) bool {
|
func checkRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *user_model.User, unitType unit.Type) bool {
|
||||||
if user.IsAdmin {
|
if user != nil && user.IsAdmin {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
perm, err := getUserRepoPermission(ctx, repo, user)
|
perm, err := getUserRepoPermission(ctx, repo, user)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
package repo
|
package repo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
@@ -115,6 +116,13 @@ func UpdateMirror(m *Mirror) error {
|
|||||||
return updateMirror(db.GetEngine(db.DefaultContext), m)
|
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
|
// DeleteMirrorByRepoID deletes a mirror by repoID
|
||||||
func DeleteMirrorByRepoID(repoID int64) error {
|
func DeleteMirrorByRepoID(repoID int64) error {
|
||||||
_, err := db.GetEngine(db.DefaultContext).Delete(&Mirror{RepoID: repoID})
|
_, err := db.GetEngine(db.DefaultContext).Delete(&Mirror{RepoID: repoID})
|
||||||
|
|||||||
@@ -233,14 +233,28 @@ func teamUnitsRepoCond(id string, userID, orgID, teamID int64, units ...unit.Typ
|
|||||||
builder.Select("repo_id").From("team_repo").Where(
|
builder.Select("repo_id").From("team_repo").Where(
|
||||||
builder.Eq{
|
builder.Eq{
|
||||||
"team_id": teamID,
|
"team_id": teamID,
|
||||||
}.And(
|
}.And(builder.Or(
|
||||||
|
// Check if the user is member of the team.
|
||||||
builder.In(
|
builder.In(
|
||||||
"team_id", builder.Select("team_id").From("team_user").Where(
|
"team_id", builder.Select("team_id").From("team_user").Where(
|
||||||
builder.Eq{
|
builder.Eq{
|
||||||
"uid": userID,
|
"uid": userID,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)).And(
|
),
|
||||||
|
// Check if the user is in the owner team of the organisation.
|
||||||
|
builder.Exists(builder.Select("team_id").From("team_user").
|
||||||
|
Where(builder.Eq{
|
||||||
|
"org_id": orgID,
|
||||||
|
"team_id": builder.Select("id").From("team").Where(
|
||||||
|
builder.Eq{
|
||||||
|
"org_id": orgID,
|
||||||
|
"lower_name": strings.ToLower(ownerTeamName),
|
||||||
|
}),
|
||||||
|
"uid": userID,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)).And(
|
||||||
builder.In(
|
builder.In(
|
||||||
"team_id", builder.Select("team_id").From("team_unit").Where(
|
"team_id", builder.Select("team_id").From("team_unit").Where(
|
||||||
builder.Eq{
|
builder.Eq{
|
||||||
|
|||||||
@@ -175,8 +175,10 @@ func init() {
|
|||||||
|
|
||||||
checkForActionConsistency := func(t assert.TestingT, bean interface{}) {
|
checkForActionConsistency := func(t assert.TestingT, bean interface{}) {
|
||||||
action := reflectionWrap(bean)
|
action := reflectionWrap(bean)
|
||||||
repoRow := AssertExistsAndLoadMap(t, "repository", builder.Eq{"id": action.int("RepoID")})
|
if action.int("RepoID") != 1700 { // dangling intentional
|
||||||
assert.Equal(t, parseBool(repoRow["is_private"]), action.bool("IsPrivate"), "action: %+v", action)
|
repoRow := AssertExistsAndLoadMap(t, "repository", builder.Eq{"id": action.int("RepoID")})
|
||||||
|
assert.Equal(t, parseBool(repoRow["is_private"]), action.bool("IsPrivate"), "action: %+v", action)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
consistencyCheckMap["user"] = checkForUserConsistency
|
consistencyCheckMap["user"] = checkForUserConsistency
|
||||||
|
|||||||
@@ -13,11 +13,13 @@ import (
|
|||||||
_ "image/jpeg" // Needed for jpeg support
|
_ "image/jpeg" // Needed for jpeg support
|
||||||
|
|
||||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||||
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/structs"
|
"code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"xorm.io/builder"
|
||||||
)
|
)
|
||||||
@@ -82,6 +84,11 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
|
|||||||
}
|
}
|
||||||
// ***** END: Follow *****
|
// ***** END: Follow *****
|
||||||
|
|
||||||
|
if _, err := db.GetEngine(ctx).In("grant_id", builder.Select("id").From("oauth2_grant").Where(builder.Eq{"oauth2_grant.user_id": u.ID})).
|
||||||
|
Delete(&auth_model.OAuth2AuthorizationCode{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if err = deleteBeans(e,
|
if err = deleteBeans(e,
|
||||||
&AccessToken{UID: u.ID},
|
&AccessToken{UID: u.ID},
|
||||||
&Collaboration{UserID: u.ID},
|
&Collaboration{UserID: u.ID},
|
||||||
@@ -99,6 +106,8 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
|
|||||||
&Collaboration{UserID: u.ID},
|
&Collaboration{UserID: u.ID},
|
||||||
&Stopwatch{UserID: u.ID},
|
&Stopwatch{UserID: u.ID},
|
||||||
&user_model.Setting{UserID: u.ID},
|
&user_model.Setting{UserID: u.ID},
|
||||||
|
&auth_model.OAuth2Application{UID: u.ID},
|
||||||
|
&auth_model.OAuth2Grant{UserID: u.ID},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return fmt.Errorf("deleteBeans: %v", err)
|
return fmt.Errorf("deleteBeans: %v", err)
|
||||||
}
|
}
|
||||||
@@ -130,6 +139,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 *****
|
// ***** START: PublicKey *****
|
||||||
if _, err = e.Delete(&asymkey_model.PublicKey{OwnerID: u.ID}); err != nil {
|
if _, err = e.Delete(&asymkey_model.PublicKey{OwnerID: u.ID}); err != nil {
|
||||||
return fmt.Errorf("deletePublicKeys: %v", err)
|
return fmt.Errorf("deletePublicKeys: %v", err)
|
||||||
|
|||||||
@@ -622,7 +622,14 @@ func IsUsableUsername(name string) error {
|
|||||||
|
|
||||||
// CreateUserOverwriteOptions are an optional options who overwrite system defaults on user creation
|
// CreateUserOverwriteOptions are an optional options who overwrite system defaults on user creation
|
||||||
type CreateUserOverwriteOptions struct {
|
type CreateUserOverwriteOptions struct {
|
||||||
Visibility structs.VisibleType
|
KeepEmailPrivate util.OptionalBool
|
||||||
|
Visibility *structs.VisibleType
|
||||||
|
AllowCreateOrganization util.OptionalBool
|
||||||
|
EmailNotificationsPreference *string
|
||||||
|
MaxRepoCreation *int
|
||||||
|
Theme *string
|
||||||
|
IsRestricted util.OptionalBool
|
||||||
|
IsActive util.OptionalBool
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateUser creates record of a new user.
|
// CreateUser creates record of a new user.
|
||||||
@@ -638,10 +645,36 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e
|
|||||||
u.EmailNotificationsPreference = setting.Admin.DefaultEmailNotification
|
u.EmailNotificationsPreference = setting.Admin.DefaultEmailNotification
|
||||||
u.MaxRepoCreation = -1
|
u.MaxRepoCreation = -1
|
||||||
u.Theme = setting.UI.DefaultTheme
|
u.Theme = setting.UI.DefaultTheme
|
||||||
|
u.IsRestricted = setting.Service.DefaultUserIsRestricted
|
||||||
|
u.IsActive = !(setting.Service.RegisterEmailConfirm || setting.Service.RegisterManualConfirm)
|
||||||
|
|
||||||
// overwrite defaults if set
|
// overwrite defaults if set
|
||||||
if len(overwriteDefault) != 0 && overwriteDefault[0] != nil {
|
if len(overwriteDefault) != 0 && overwriteDefault[0] != nil {
|
||||||
u.Visibility = overwriteDefault[0].Visibility
|
overwrite := overwriteDefault[0]
|
||||||
|
if !overwrite.KeepEmailPrivate.IsNone() {
|
||||||
|
u.KeepEmailPrivate = overwrite.KeepEmailPrivate.IsTrue()
|
||||||
|
}
|
||||||
|
if overwrite.Visibility != nil {
|
||||||
|
u.Visibility = *overwrite.Visibility
|
||||||
|
}
|
||||||
|
if !overwrite.AllowCreateOrganization.IsNone() {
|
||||||
|
u.AllowCreateOrganization = overwrite.AllowCreateOrganization.IsTrue()
|
||||||
|
}
|
||||||
|
if overwrite.EmailNotificationsPreference != nil {
|
||||||
|
u.EmailNotificationsPreference = *overwrite.EmailNotificationsPreference
|
||||||
|
}
|
||||||
|
if overwrite.MaxRepoCreation != nil {
|
||||||
|
u.MaxRepoCreation = *overwrite.MaxRepoCreation
|
||||||
|
}
|
||||||
|
if overwrite.Theme != nil {
|
||||||
|
u.Theme = *overwrite.Theme
|
||||||
|
}
|
||||||
|
if !overwrite.IsRestricted.IsNone() {
|
||||||
|
u.IsRestricted = overwrite.IsRestricted.IsTrue()
|
||||||
|
}
|
||||||
|
if !overwrite.IsActive.IsNone() {
|
||||||
|
u.IsActive = overwrite.IsActive.IsTrue()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate data
|
// validate data
|
||||||
|
|||||||
@@ -497,14 +497,19 @@ func GetSystemOrDefaultWebhook(id int64) (*Webhook, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetSystemWebhooks returns all admin system webhooks.
|
// GetSystemWebhooks returns all admin system webhooks.
|
||||||
func GetSystemWebhooks() ([]*Webhook, error) {
|
func GetSystemWebhooks(isActive util.OptionalBool) ([]*Webhook, error) {
|
||||||
return getSystemWebhooks(db.GetEngine(db.DefaultContext))
|
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)
|
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.
|
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)
|
Find(&webhooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -288,6 +288,7 @@ func APIContexter() func(http.Handler) http.Handler {
|
|||||||
},
|
},
|
||||||
Org: &APIOrganization{},
|
Org: &APIOrganization{},
|
||||||
}
|
}
|
||||||
|
defer ctx.Close()
|
||||||
|
|
||||||
ctx.Req = WithAPIContext(WithContext(req, ctx.Context), &ctx)
|
ctx.Req = WithAPIContext(WithContext(req, ctx.Context), &ctx)
|
||||||
ctx.csrf = Csrfer(csrfOpts, ctx.Context)
|
ctx.csrf = Csrfer(csrfOpts, ctx.Context)
|
||||||
|
|||||||
@@ -71,6 +71,16 @@ type Context struct {
|
|||||||
Org *Organization
|
Org *Organization
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close frees all resources hold by Context
|
||||||
|
func (ctx *Context) Close() error {
|
||||||
|
var err error
|
||||||
|
if ctx.Req != nil && ctx.Req.MultipartForm != nil {
|
||||||
|
err = ctx.Req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
|
||||||
|
}
|
||||||
|
// TODO: close opened repo, and more
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// TrHTMLEscapeArgs runs Tr but pre-escapes all arguments with html.EscapeString.
|
// TrHTMLEscapeArgs runs Tr but pre-escapes all arguments with html.EscapeString.
|
||||||
// This is useful if the locale message is intended to only produce HTML content.
|
// This is useful if the locale message is intended to only produce HTML content.
|
||||||
func (ctx *Context) TrHTMLEscapeArgs(msg string, args ...string) string {
|
func (ctx *Context) TrHTMLEscapeArgs(msg string, args ...string) string {
|
||||||
@@ -181,6 +191,12 @@ func (ctx *Context) RedirectToFirst(location ...string) {
|
|||||||
continue
|
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)
|
u, err := url.Parse(loc)
|
||||||
if err != nil || ((u.Scheme != "" || u.Host != "") && !strings.HasPrefix(strings.ToLower(loc), strings.ToLower(setting.AppURL))) {
|
if err != nil || ((u.Scheme != "" || u.Host != "") && !strings.HasPrefix(strings.ToLower(loc), strings.ToLower(setting.AppURL))) {
|
||||||
continue
|
continue
|
||||||
@@ -637,6 +653,8 @@ func Contexter() func(next http.Handler) http.Handler {
|
|||||||
"RunModeIsProd": setting.IsProd,
|
"RunModeIsProd": setting.IsProd,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
defer ctx.Close()
|
||||||
|
|
||||||
// PageData is passed by reference, and it will be rendered to `window.config.pageData` in `head.tmpl` for JavaScript modules
|
// PageData is passed by reference, and it will be rendered to `window.config.pageData` in `head.tmpl` for JavaScript modules
|
||||||
ctx.PageData = map[string]interface{}{}
|
ctx.PageData = map[string]interface{}{}
|
||||||
ctx.Data["PageData"] = ctx.PageData
|
ctx.Data["PageData"] = ctx.PageData
|
||||||
|
|||||||
@@ -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 {
|
if needsNew {
|
||||||
// FIXME: actionId.
|
// FIXME: actionId.
|
||||||
x.Token = GenerateToken(x.Secret, x.ID, "POST")
|
x.Token = GenerateToken(x.Secret, x.ID, "POST")
|
||||||
|
|||||||
@@ -70,12 +70,6 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
|
|||||||
org := ctx.Org.Organization
|
org := ctx.Org.Organization
|
||||||
ctx.Data["Org"] = org
|
ctx.Data["Org"] = org
|
||||||
|
|
||||||
teams, err := org.LoadTeams()
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("LoadTeams", err)
|
|
||||||
}
|
|
||||||
ctx.Data["OrgTeams"] = teams
|
|
||||||
|
|
||||||
// Admin has super access.
|
// Admin has super access.
|
||||||
if ctx.IsSigned && ctx.User.IsAdmin {
|
if ctx.IsSigned && ctx.User.IsAdmin {
|
||||||
ctx.Org.IsOwner = true
|
ctx.Org.IsOwner = true
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ func PrivateContexter() func(http.Handler) http.Handler {
|
|||||||
Data: map[string]interface{}{},
|
Data: map[string]interface{}{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
defer ctx.Close()
|
||||||
|
|
||||||
ctx.Req = WithPrivateContext(req, ctx)
|
ctx.Req = WithPrivateContext(req, ctx)
|
||||||
next.ServeHTTP(ctx.Resp, ctx.Req)
|
next.ServeHTTP(ctx.Resp, ctx.Req)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -410,6 +410,12 @@ func RepoIDAssignment() func(ctx *Context) {
|
|||||||
|
|
||||||
// RepoAssignment returns a middleware to handle repository assignment
|
// RepoAssignment returns a middleware to handle repository assignment
|
||||||
func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
|
func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
|
||||||
|
if _, repoAssignmentOnce := ctx.Data["repoAssignmentExecuted"]; repoAssignmentOnce {
|
||||||
|
log.Trace("RepoAssignment was exec already, skipping second call ...")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["repoAssignmentExecuted"] = true
|
||||||
|
|
||||||
var (
|
var (
|
||||||
owner *user_model.User
|
owner *user_model.User
|
||||||
err error
|
err error
|
||||||
@@ -440,6 +446,26 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
|
|||||||
ctx.Repo.Owner = owner
|
ctx.Repo.Owner = owner
|
||||||
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
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.
|
// Get repository.
|
||||||
repo, err := repo_model.GetRepositoryByName(owner.ID, repoName)
|
repo, err := repo_model.GetRepositoryByName(owner.ID, repoName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -572,6 +598,9 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
|
|||||||
ctx.ServerError("RepoAssignment Invalid repo "+repo_model.RepoPath(userName, repoName), err)
|
ctx.ServerError("RepoAssignment Invalid repo "+repo_model.RepoPath(userName, repoName), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if ctx.Repo.GitRepo != nil {
|
||||||
|
ctx.Repo.GitRepo.Close()
|
||||||
|
}
|
||||||
ctx.Repo.GitRepo = gitRepo
|
ctx.Repo.GitRepo = gitRepo
|
||||||
|
|
||||||
// We opened it, we should close it
|
// We opened it, we should close it
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ func ToAPIPullRequest(pr *models.PullRequest, doer *user_model.User) *api.PullRe
|
|||||||
PatchURL: pr.Issue.PatchURL(),
|
PatchURL: pr.Issue.PatchURL(),
|
||||||
HasMerged: pr.HasMerged,
|
HasMerged: pr.HasMerged,
|
||||||
MergeBase: pr.MergeBase,
|
MergeBase: pr.MergeBase,
|
||||||
|
Mergeable: pr.Mergeable(),
|
||||||
Deadline: apiIssue.Deadline,
|
Deadline: apiIssue.Deadline,
|
||||||
Created: pr.Issue.CreatedUnix.AsTimePtr(),
|
Created: pr.Issue.CreatedUnix.AsTimePtr(),
|
||||||
Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
|
Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
|
||||||
@@ -190,10 +191,6 @@ func ToAPIPullRequest(pr *models.PullRequest, doer *user_model.User) *api.PullRe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if pr.Status != models.PullRequestStatusChecking {
|
|
||||||
mergeable := !(pr.Status == models.PullRequestStatusConflict || pr.Status == models.PullRequestStatusError) && !pr.IsWorkInProgress()
|
|
||||||
apiPullRequest.Mergeable = mergeable
|
|
||||||
}
|
|
||||||
if pr.HasMerged {
|
if pr.HasMerged {
|
||||||
apiPullRequest.Merged = pr.MergedUnix.AsTimePtr()
|
apiPullRequest.Merged = pr.MergedUnix.AsTimePtr()
|
||||||
apiPullRequest.MergedCommitID = &pr.MergedCommitID
|
apiPullRequest.MergedCommitID = &pr.MergedCommitID
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ func checkAuthorizedKeys(logger log.Logger, autofix bool) error {
|
|||||||
"authorized_keys file %q is out of date.\nRegenerate it with:\n\t\"%s\"\nor\n\t\"%s\"",
|
"authorized_keys file %q is out of date.\nRegenerate it with:\n\t\"%s\"\nor\n\t\"%s\"",
|
||||||
fPath,
|
fPath,
|
||||||
"gitea admin regenerate keys",
|
"gitea admin regenerate keys",
|
||||||
"gitea doctor --run authorized_keys --fix")
|
"gitea doctor --run authorized-keys --fix")
|
||||||
return fmt.Errorf(`authorized_keys is out of date and should be regenerated with "gitea admin regenerate keys" or "gitea doctor --run authorized_keys --fix"`)
|
return fmt.Errorf(`authorized_keys is out of date and should be regenerated with "gitea admin regenerate keys" or "gitea doctor --run authorized-keys --fix"`)
|
||||||
}
|
}
|
||||||
logger.Warn("authorized_keys is out of date. Attempting rewrite...")
|
logger.Warn("authorized_keys is out of date. Attempting rewrite...")
|
||||||
err = asymkey_model.RewriteAllPublicKeys()
|
err = asymkey_model.RewriteAllPublicKeys()
|
||||||
|
|||||||
@@ -142,6 +142,12 @@ func checkDBConsistency(logger log.Logger, autofix bool) error {
|
|||||||
Fixer: models.FixIssueLabelWithOutsideLabels,
|
Fixer: models.FixIssueLabelWithOutsideLabels,
|
||||||
FixedMessage: "Removed",
|
FixedMessage: "Removed",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "Action with created_unix set as an empty string",
|
||||||
|
Counter: models.CountActionCreatedUnixString,
|
||||||
|
Fixer: models.FixActionCreatedUnixString,
|
||||||
|
FixedMessage: "Set to zero",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: function to recalc all counters
|
// TODO: function to recalc all counters
|
||||||
@@ -177,6 +183,18 @@ func checkDBConsistency(logger log.Logger, autofix bool) error {
|
|||||||
// find access without repository
|
// find access without repository
|
||||||
genericOrphanCheck("Access entries without existing repository",
|
genericOrphanCheck("Access entries without existing repository",
|
||||||
"access", "repository", "access.repo_id=repository.id"),
|
"access", "repository", "access.repo_id=repository.id"),
|
||||||
|
// find action without repository
|
||||||
|
genericOrphanCheck("Action entries without existing repository",
|
||||||
|
"action", "repository", "action.repo_id=repository.id"),
|
||||||
|
// find OAuth2Grant without existing user
|
||||||
|
genericOrphanCheck("Orphaned OAuth2Grant without existing User",
|
||||||
|
"oauth2_grant", "user", "oauth2_grant.user_id=user.id"),
|
||||||
|
// find OAuth2Application without existing user
|
||||||
|
genericOrphanCheck("Orphaned OAuth2Application without existing User",
|
||||||
|
"oauth2_application", "user", "oauth2_application.uid=user.id"),
|
||||||
|
// find OAuth2AuthorizationCode without existing OAuth2Grant
|
||||||
|
genericOrphanCheck("Orphaned OAuth2AuthorizationCode without existing OAuth2Grant",
|
||||||
|
"oauth2_authorization_code", "oauth2_grant", "oauth2_authorization_code.grant_id=oauth2_grant.id"),
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, c := range consistencyChecks {
|
for _, c := range consistencyChecks {
|
||||||
|
|||||||
@@ -54,6 +54,12 @@ func CatFileBatchCheck(ctx context.Context, repoPath string) (WriteCloserError,
|
|||||||
<-closed
|
<-closed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure cancel is called as soon as the provided context is cancelled
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
_, filename, line, _ := runtime.Caller(2)
|
_, filename, line, _ := runtime.Caller(2)
|
||||||
filename = strings.TrimPrefix(filename, callerPrefix)
|
filename = strings.TrimPrefix(filename, callerPrefix)
|
||||||
|
|
||||||
@@ -93,6 +99,12 @@ func CatFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi
|
|||||||
<-closed
|
<-closed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure cancel is called as soon as the provided context is cancelled
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
_, filename, line, _ := runtime.Caller(2)
|
_, filename, line, _ := runtime.Caller(2)
|
||||||
filename = strings.TrimPrefix(filename, callerPrefix)
|
filename = strings.TrimPrefix(filename, callerPrefix)
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/process"
|
"code.gitea.io/gitea/modules/process"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -32,10 +33,11 @@ const DefaultLocale = "C"
|
|||||||
|
|
||||||
// Command represents a command with its subcommands or arguments.
|
// Command represents a command with its subcommands or arguments.
|
||||||
type Command struct {
|
type Command struct {
|
||||||
name string
|
name string
|
||||||
args []string
|
args []string
|
||||||
parentContext context.Context
|
parentContext context.Context
|
||||||
desc string
|
desc string
|
||||||
|
globalArgsLength int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Command) String() string {
|
func (c *Command) String() string {
|
||||||
@@ -56,9 +58,10 @@ func NewCommandContext(ctx context.Context, args ...string) *Command {
|
|||||||
cargs := make([]string, len(GlobalCommandArgs))
|
cargs := make([]string, len(GlobalCommandArgs))
|
||||||
copy(cargs, GlobalCommandArgs)
|
copy(cargs, GlobalCommandArgs)
|
||||||
return &Command{
|
return &Command{
|
||||||
name: GitExecutable,
|
name: GitExecutable,
|
||||||
args: append(cargs, args...),
|
args: append(cargs, args...),
|
||||||
parentContext: ctx,
|
parentContext: ctx,
|
||||||
|
globalArgsLength: len(GlobalCommandArgs),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +148,21 @@ func (c *Command) RunWithContext(rc *RunContext) error {
|
|||||||
|
|
||||||
desc := c.desc
|
desc := c.desc
|
||||||
if 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)
|
ctx, cancel, finished := process.GetManager().AddContextTimeout(c.parentContext, rc.Timeout, desc)
|
||||||
|
|||||||
@@ -112,8 +112,8 @@ func SetExecutablePath(path string) error {
|
|||||||
|
|
||||||
// VersionInfo returns git version information
|
// VersionInfo returns git version information
|
||||||
func VersionInfo() string {
|
func VersionInfo() string {
|
||||||
var format = "Git Version: %s"
|
format := "Git Version: %s"
|
||||||
var args = []interface{}{gitVersion.Original()}
|
args := []interface{}{gitVersion.Original()}
|
||||||
// Since git wire protocol has been released from git v2.18
|
// Since git wire protocol has been released from git v2.18
|
||||||
if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
|
if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
|
||||||
format += ", Wire Protocol %s Enabled"
|
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
|
// By default partial clones are disabled, enable them from git v2.22
|
||||||
if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil {
|
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
|
// Save current git version on init to gitVersion otherwise it would require an RWMutex
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/proxy"
|
"code.gitea.io/gitea/modules/proxy"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GPGSettings represents the default GPG settings for this repository
|
// 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
|
// CloneRepoOptions options when clone a repository
|
||||||
type CloneRepoOptions struct {
|
type CloneRepoOptions struct {
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
Mirror bool
|
Mirror bool
|
||||||
Bare bool
|
Bare bool
|
||||||
Quiet bool
|
Quiet bool
|
||||||
Branch string
|
Branch string
|
||||||
Shared bool
|
Shared bool
|
||||||
NoCheckout bool
|
NoCheckout bool
|
||||||
Depth int
|
Depth int
|
||||||
Filter string
|
Filter string
|
||||||
|
SkipTLSVerify bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone clones original repository to target path.
|
// 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")
|
cmd := NewCommandContextNoGlobals(ctx, args...).AddArguments("clone")
|
||||||
|
if opts.SkipTLSVerify {
|
||||||
|
cmd.AddArguments("-c", "http.sslVerify=false")
|
||||||
|
}
|
||||||
if opts.Mirror {
|
if opts.Mirror {
|
||||||
cmd.AddArguments("--mirror")
|
cmd.AddArguments("--mirror")
|
||||||
}
|
}
|
||||||
@@ -154,6 +159,12 @@ func CloneWithArgs(ctx context.Context, from, to string, args []string, opts Clo
|
|||||||
}
|
}
|
||||||
cmd.AddArguments("--", from, to)
|
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 {
|
if opts.Timeout <= 0 {
|
||||||
opts.Timeout = -1
|
opts.Timeout = -1
|
||||||
}
|
}
|
||||||
@@ -230,6 +241,11 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error {
|
|||||||
if len(opts.Branch) > 0 {
|
if len(opts.Branch) > 0 {
|
||||||
cmd.AddArguments(opts.Branch)
|
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
|
var outbuf, errbuf strings.Builder
|
||||||
|
|
||||||
if opts.Timeout == 0 {
|
if opts.Timeout == 0 {
|
||||||
|
|||||||
@@ -119,12 +119,10 @@ type CheckAttributeReader struct {
|
|||||||
env []string
|
env []string
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
running chan struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the cmd
|
// Init initializes the cmd
|
||||||
func (c *CheckAttributeReader) Init(ctx context.Context) error {
|
func (c *CheckAttributeReader) Init(ctx context.Context) error {
|
||||||
c.running = make(chan struct{})
|
|
||||||
cmdArgs := []string{"check-attr", "--stdin", "-z"}
|
cmdArgs := []string{"check-attr", "--stdin", "-z"}
|
||||||
|
|
||||||
if len(c.IndexFile) > 0 && CheckGitVersionAtLeast("1.7.8") == nil {
|
if len(c.IndexFile) > 0 && CheckGitVersionAtLeast("1.7.8") == nil {
|
||||||
@@ -183,14 +181,7 @@ func (c *CheckAttributeReader) Run() error {
|
|||||||
_ = c.stdOut.Close()
|
_ = c.stdOut.Close()
|
||||||
}()
|
}()
|
||||||
stdErr := new(bytes.Buffer)
|
stdErr := new(bytes.Buffer)
|
||||||
err := c.cmd.RunInDirTimeoutEnvFullPipelineFunc(c.env, -1, c.Repo.Path, c.stdOut, stdErr, c.stdinReader, func(_ context.Context, _ context.CancelFunc) error {
|
err := c.cmd.RunInDirTimeoutEnvFullPipeline(c.env, -1, c.Repo.Path, c.stdOut, stdErr, c.stdinReader)
|
||||||
select {
|
|
||||||
case <-c.running:
|
|
||||||
default:
|
|
||||||
close(c.running)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil && // If there is an error we need to return but:
|
if err != nil && // If there is an error we need to return but:
|
||||||
c.ctx.Err() != err && // 1. Ignore the context error if the context is cancelled or exceeds the deadline (RunWithContext could return c.ctx.Err() which is Canceled or DeadlineExceeded)
|
c.ctx.Err() != err && // 1. Ignore the context error if the context is cancelled or exceeds the deadline (RunWithContext could return c.ctx.Err() which is Canceled or DeadlineExceeded)
|
||||||
err.Error() != "signal: killed" { // 2. We should not pass up errors due to the program being killed
|
err.Error() != "signal: killed" { // 2. We should not pass up errors due to the program being killed
|
||||||
@@ -210,7 +201,7 @@ func (c *CheckAttributeReader) CheckPath(path string) (rs map[string]string, err
|
|||||||
select {
|
select {
|
||||||
case <-c.ctx.Done():
|
case <-c.ctx.Done():
|
||||||
return nil, c.ctx.Err()
|
return nil, c.ctx.Err()
|
||||||
case <-c.running:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = c.stdinWriter.Write([]byte(path + "\x00")); err != nil {
|
if _, err = c.stdinWriter.Write([]byte(path + "\x00")); err != nil {
|
||||||
@@ -237,11 +228,6 @@ func (c *CheckAttributeReader) CheckPath(path string) (rs map[string]string, err
|
|||||||
func (c *CheckAttributeReader) Close() error {
|
func (c *CheckAttributeReader) Close() error {
|
||||||
c.cancel()
|
c.cancel()
|
||||||
err := c.stdinWriter.Close()
|
err := c.stdinWriter.Close()
|
||||||
select {
|
|
||||||
case <-c.running:
|
|
||||||
default:
|
|
||||||
close(c.running)
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
"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.
|
// 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
|
// 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)
|
repo, err := OpenRepositoryCtx(ctx, repoPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -97,9 +99,45 @@ func WalkReferences(ctx context.Context, repoPath string, walkfn func(string) er
|
|||||||
defer iter.Close()
|
defer iter.Close()
|
||||||
|
|
||||||
err = iter.ForEach(func(ref *plumbing.Reference) error {
|
err = iter.ForEach(func(ref *plumbing.Reference) error {
|
||||||
err := walkfn(string(ref.Name()))
|
err := walkfn(ref.Hash().String(), string(ref.Name()))
|
||||||
i++
|
i++
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
return i, 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
|
// 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)
|
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
|
// 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) {
|
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)
|
branchName = strings.TrimPrefix(branchName, prefix)
|
||||||
branchNames = append(branchNames, branchName)
|
branchNames = append(branchNames, branchName)
|
||||||
|
|
||||||
@@ -83,7 +99,7 @@ func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit
|
|||||||
return
|
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()
|
stdoutReader, stdoutWriter := io.Pipe()
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = stdoutReader.Close()
|
_ = stdoutReader.Close()
|
||||||
@@ -125,11 +141,7 @@ func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, wal
|
|||||||
for limit == 0 || i < skip+limit {
|
for limit == 0 || i < skip+limit {
|
||||||
// The output of show-ref is simply a list:
|
// The output of show-ref is simply a list:
|
||||||
// <sha> SP <ref> LF
|
// <sha> SP <ref> LF
|
||||||
_, err := bufReader.ReadSlice(' ')
|
sha, err := bufReader.ReadString(' ')
|
||||||
for err == bufio.ErrBufferFull {
|
|
||||||
// This shouldn't happen but we'll tolerate it for the sake of peace
|
|
||||||
_, err = bufReader.ReadSlice(' ')
|
|
||||||
}
|
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
@@ -149,7 +161,12 @@ func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, wal
|
|||||||
if len(branchName) > 0 {
|
if len(branchName) > 0 {
|
||||||
branchName = branchName[:len(branchName)-1]
|
branchName = branchName[:len(branchName)-1]
|
||||||
}
|
}
|
||||||
err = walkfn(branchName)
|
|
||||||
|
if len(sha) > 0 {
|
||||||
|
sha = sha[:len(sha)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = walkfn(sha, branchName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|||||||
21
modules/git/repo_commitgraph.go
Normal file
21
modules/git/repo_commitgraph.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,69 +33,6 @@ func (repo *Repository) CreateAnnotatedTag(name, message, revision string) error
|
|||||||
return err
|
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
|
// GetTagNameBySHA returns the name of a tag from its tag object SHA or commit SHA
|
||||||
func (repo *Repository) GetTagNameBySHA(sha string) (string, error) {
|
func (repo *Repository) GetTagNameBySHA(sha string) (string, error) {
|
||||||
if len(sha) < 5 {
|
if len(sha) < 5 {
|
||||||
@@ -159,6 +95,20 @@ func (repo *Repository) GetTag(name string) (*Tag, error) {
|
|||||||
return tag, nil
|
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.
|
// GetTagInfos returns all tag infos of the repository.
|
||||||
func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
|
func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
|
||||||
// TODO this a slow implementation, makes one git command per tag
|
// 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
|
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
|
// GetAnnotatedTag returns a Git tag by its SHA, must be an annotated tag
|
||||||
func (repo *Repository) GetAnnotatedTag(sha string) (*Tag, error) {
|
func (repo *Repository) GetAnnotatedTag(sha string) (*Tag, error) {
|
||||||
id, err := NewIDFromString(sha)
|
id, err := NewIDFromString(sha)
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ package git
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -53,3 +55,83 @@ func (repo *Repository) GetTags(skip, limit int) ([]string, error) {
|
|||||||
|
|
||||||
return tagNames, nil
|
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
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
)
|
||||||
|
|
||||||
// IsTagExist returns true if given tag exists in the repository.
|
// IsTagExist returns true if given tag exists in the repository.
|
||||||
func (repo *Repository) IsTagExist(name string) bool {
|
func (repo *Repository) IsTagExist(name string) bool {
|
||||||
if name == "" {
|
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)
|
tags, _, err = callShowRef(repo.Ctx, repo.Path, TagPrefix, "--tags", skip, limit)
|
||||||
return
|
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
|
- go test -v -race -coverprofile=coverage.txt -covermode=atomic
|
||||||
`,
|
`,
|
||||||
want: []string{
|
want: []string{
|
||||||
`<span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</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 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 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><span class="line"><span class="cl">`,
|
||||||
</span>`,
|
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">steps</span><span class="p">:</span>`,
|
||||||
`<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 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 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 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 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 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 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 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 class="w"> </span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span><span class="w">
|
</span></span></span>`,
|
||||||
</span>`,
|
|
||||||
`<span class="w">
|
`<span class="w">
|
||||||
</span>`,
|
</span>`,
|
||||||
},
|
},
|
||||||
@@ -76,20 +75,19 @@ steps:
|
|||||||
- go test -v -race -coverprofile=coverage.txt -covermode=atomic
|
- go test -v -race -coverprofile=coverage.txt -covermode=atomic
|
||||||
`,
|
`,
|
||||||
want: []string{
|
want: []string{
|
||||||
`<span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</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 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 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><span class="line"><span class="cl">`,
|
||||||
</span>`,
|
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">steps</span><span class="p">:</span>`,
|
||||||
`<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 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 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 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 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 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 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 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 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>`,
|
||||||
`<span class="w"> </span>`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,13 +127,18 @@ func (hl *HostMatchList) checkIP(ip net.IP) bool {
|
|||||||
|
|
||||||
// MatchHostName checks if the host matches an allow/deny(block) list
|
// MatchHostName checks if the host matches an allow/deny(block) list
|
||||||
func (hl *HostMatchList) MatchHostName(host string) bool {
|
func (hl *HostMatchList) MatchHostName(host string) bool {
|
||||||
|
hostname, _, err := net.SplitHostPort(host)
|
||||||
|
if err != nil {
|
||||||
|
hostname = host
|
||||||
|
}
|
||||||
|
|
||||||
if hl == nil {
|
if hl == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if hl.checkPattern(host) {
|
if hl.checkPattern(hostname) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if ip := net.ParseIP(host); ip != nil {
|
if ip := net.ParseIP(hostname); ip != nil {
|
||||||
return hl.checkIP(ip)
|
return hl.checkIP(ip)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ func TestHostOrIPMatchesList(t *testing.T) {
|
|||||||
|
|
||||||
{"", net.ParseIP("10.0.1.1"), true},
|
{"", net.ParseIP("10.0.1.1"), true},
|
||||||
{"10.0.1.1", nil, true},
|
{"10.0.1.1", nil, true},
|
||||||
|
{"10.0.1.1:8080", nil, true},
|
||||||
{"", net.ParseIP("192.168.1.1"), true},
|
{"", net.ParseIP("192.168.1.1"), true},
|
||||||
{"192.168.1.1", nil, true},
|
{"192.168.1.1", nil, true},
|
||||||
{"", net.ParseIP("fd00::1"), true},
|
{"", net.ParseIP("fd00::1"), true},
|
||||||
@@ -48,6 +49,7 @@ func TestHostOrIPMatchesList(t *testing.T) {
|
|||||||
|
|
||||||
{"mydomain.com", net.IPv4zero, false},
|
{"mydomain.com", net.IPv4zero, false},
|
||||||
{"sub.mydomain.com", net.IPv4zero, true},
|
{"sub.mydomain.com", net.IPv4zero, true},
|
||||||
|
{"sub.mydomain.com:8080", net.IPv4zero, true},
|
||||||
|
|
||||||
{"", net.ParseIP("169.254.1.1"), true},
|
{"", net.ParseIP("169.254.1.1"), true},
|
||||||
{"169.254.1.1", nil, true},
|
{"169.254.1.1", nil, true},
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ func Init() {
|
|||||||
log.Info("PID: %d Repository Indexer closed", os.Getpid())
|
log.Info("PID: %d Repository Indexer closed", os.Getpid())
|
||||||
})
|
})
|
||||||
|
|
||||||
waitChannel := make(chan time.Duration)
|
waitChannel := make(chan time.Duration, 1)
|
||||||
|
|
||||||
// Create the Queue
|
// Create the Queue
|
||||||
switch setting.Indexer.RepoType {
|
switch setting.Indexer.RepoType {
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ var (
|
|||||||
// InitIssueIndexer initialize issue indexer, syncReindex is true then reindex until
|
// InitIssueIndexer initialize issue indexer, syncReindex is true then reindex until
|
||||||
// all issue index done.
|
// all issue index done.
|
||||||
func InitIssueIndexer(syncReindex bool) {
|
func InitIssueIndexer(syncReindex bool) {
|
||||||
waitChannel := make(chan time.Duration)
|
waitChannel := make(chan time.Duration, 1)
|
||||||
|
|
||||||
// Create the Queue
|
// Create the Queue
|
||||||
switch setting.Indexer.IssueType {
|
switch setting.Indexer.IssueType {
|
||||||
@@ -272,7 +272,7 @@ func populateIssueIndexer(ctx context.Context) {
|
|||||||
// UpdateRepoIndexer add/update all issues of the repositories
|
// UpdateRepoIndexer add/update all issues of the repositories
|
||||||
func UpdateRepoIndexer(repo *repo_model.Repository) {
|
func UpdateRepoIndexer(repo *repo_model.Repository) {
|
||||||
is, err := models.Issues(&models.IssuesOptions{
|
is, err := models.Issues(&models.IssuesOptions{
|
||||||
RepoIDs: []int64{repo.ID},
|
RepoID: repo.ID,
|
||||||
IsClosed: util.OptionalBoolNone,
|
IsClosed: util.OptionalBoolNone,
|
||||||
IsPull: util.OptionalBoolNone,
|
IsPull: util.OptionalBoolNone,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,12 +5,15 @@
|
|||||||
package stats
|
package stats
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/queue"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
_ "code.gitea.io/gitea/models"
|
_ "code.gitea.io/gitea/models"
|
||||||
@@ -24,6 +27,10 @@ func TestMain(m *testing.M) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRepoStatsIndex(t *testing.T) {
|
func TestRepoStatsIndex(t *testing.T) {
|
||||||
|
if err := git.Init(context.Background()); !assert.NoError(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
setting.Cfg = ini.Empty()
|
setting.Cfg = ini.Empty()
|
||||||
|
|
||||||
@@ -32,10 +39,14 @@ func TestRepoStatsIndex(t *testing.T) {
|
|||||||
err := Init()
|
err := Init()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
|
|
||||||
repo, err := repo_model.GetRepositoryByID(1)
|
repo, err := repo_model.GetRepositoryByID(1)
|
||||||
assert.NoError(t, err)
|
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)
|
status, err := repo_model.GetIndexerStatus(repo, repo_model.RepoIndexerTypeStats)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "65f1bf27bc3bf70f64657658635e66094edbcb4d", status.CommitSha)
|
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
|
open := pos + 1
|
||||||
closes := 0
|
closes := 0
|
||||||
closure := util.FindClosure(line[pos+1:], '[', ']', false, false)
|
closure := util.FindClosure(line[pos+1:], '[', ']', false, false) //nolint
|
||||||
closes = pos + 1 + closure
|
closes = pos + 1 + closure
|
||||||
next := closes + 1
|
next := closes + 1
|
||||||
if closure > -1 {
|
if closure > -1 {
|
||||||
@@ -296,7 +296,7 @@ func (s *footnoteParser) Parse(parent ast.Node, block text.Reader, pc parser.Con
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
open := pos
|
open := pos
|
||||||
closure := util.FindClosure(line[pos:], '[', ']', false, false)
|
closure := util.FindClosure(line[pos:], '[', ']', false, false) //nolint
|
||||||
if closure < 0 {
|
if closure < 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,6 +197,11 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
|
|||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</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.
|
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) {
|
func TestTotal_RenderWiki(t *testing.T) {
|
||||||
|
|||||||
@@ -77,9 +77,9 @@ func HelloWorld() {
|
|||||||
}
|
}
|
||||||
#+end_src
|
#+end_src
|
||||||
`, `<div class="src src-go">
|
`, `<div class="src src-go">
|
||||||
<pre><code class="chroma language-go"><span class="c1">// HelloWorld prints "Hello World"
|
<pre><code class="chroma language-go"><span class="line"><span class="cl"><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></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 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="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>
|
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre>
|
||||||
</div>`)
|
</div>`)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ type HookOptions struct {
|
|||||||
GitQuarantinePath string
|
GitQuarantinePath string
|
||||||
GitPushOptions GitPushOptions
|
GitPushOptions GitPushOptions
|
||||||
PullRequestID int64
|
PullRequestID int64
|
||||||
IsDeployKey bool
|
DeployKeyID int64 // if the pusher is a DeployKey, then UserID is the repo's org user.
|
||||||
IsWiki bool
|
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
|
// ServCommandResults are the results of a call to the private route serv
|
||||||
type ServCommandResults struct {
|
type ServCommandResults struct {
|
||||||
IsWiki bool
|
IsWiki bool
|
||||||
IsDeployKey bool
|
DeployKeyID int64
|
||||||
KeyID int64
|
KeyID int64 // public key
|
||||||
KeyName string
|
KeyName string // this field is ambiguous, it can be the name of DeployKey, or the name of the PublicKey
|
||||||
UserName string
|
UserName string
|
||||||
UserEmail string
|
UserEmail string
|
||||||
UserID int64
|
UserID int64
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ import (
|
|||||||
// they use to detect if there is a block and will grow and shrink in
|
// they use to detect if there is a block and will grow and shrink in
|
||||||
// response to demand as per configuration.
|
// response to demand as per configuration.
|
||||||
type WorkerPool struct {
|
type WorkerPool struct {
|
||||||
|
// This field requires to be the first one in the struct.
|
||||||
|
// This is to allow 64 bit atomic operations on 32-bit machines.
|
||||||
|
// See: https://pkg.go.dev/sync/atomic#pkg-note-BUG & Gitea issue 19518
|
||||||
|
numInQueue int64
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
baseCtx context.Context
|
baseCtx context.Context
|
||||||
baseCtxCancel context.CancelFunc
|
baseCtxCancel context.CancelFunc
|
||||||
@@ -32,7 +36,6 @@ type WorkerPool struct {
|
|||||||
blockTimeout time.Duration
|
blockTimeout time.Duration
|
||||||
boostTimeout time.Duration
|
boostTimeout time.Duration
|
||||||
boostWorkers int
|
boostWorkers int
|
||||||
numInQueue int64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WorkerPoolConfiguration is the basic configuration for a WorkerPool
|
// WorkerPoolConfiguration is the basic configuration for a WorkerPool
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ var defaultTransformers = []transformer{
|
|||||||
{Name: "PASCAL", Transform: xstrings.ToCamelCase},
|
{Name: "PASCAL", Transform: xstrings.ToCamelCase},
|
||||||
{Name: "LOWER", Transform: strings.ToLower},
|
{Name: "LOWER", Transform: strings.ToLower},
|
||||||
{Name: "UPPER", Transform: strings.ToUpper},
|
{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 {
|
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},
|
{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 {
|
for _, e := range expansions {
|
||||||
expansionMap[e.Name] = e.Value
|
expansionMap[e.Name] = e.Value
|
||||||
for _, tr := range e.Transformers {
|
for _, tr := range e.Transformers {
|
||||||
@@ -159,7 +159,7 @@ func generateRepoCommit(repo, templateRepo, generateRepo *repo_model.Repository,
|
|||||||
|
|
||||||
if err := os.WriteFile(path,
|
if err := os.WriteFile(path,
|
||||||
[]byte(generateExpansion(string(content), templateRepo, generateRepo)),
|
[]byte(generateExpansion(string(content), templateRepo, generateRepo)),
|
||||||
0644); err != nil {
|
0o644); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -72,13 +72,18 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err = git.CloneWithContext(ctx, opts.CloneAddr, repoPath, git.CloneRepoOptions{
|
if err = git.CloneWithContext(ctx, opts.CloneAddr, repoPath, git.CloneRepoOptions{
|
||||||
Mirror: true,
|
Mirror: true,
|
||||||
Quiet: true,
|
Quiet: true,
|
||||||
Timeout: migrateTimeout,
|
Timeout: migrateTimeout,
|
||||||
|
SkipTLSVerify: setting.Migrations.SkipTLSVerify,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return repo, fmt.Errorf("Clone: %v", err)
|
return repo, fmt.Errorf("Clone: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := git.WriteCommitGraph(ctx, repoPath); err != nil {
|
||||||
|
return repo, err
|
||||||
|
}
|
||||||
|
|
||||||
if opts.Wiki {
|
if opts.Wiki {
|
||||||
wikiPath := repo_model.WikiPath(u.Name, opts.RepoName)
|
wikiPath := repo_model.WikiPath(u.Name, opts.RepoName)
|
||||||
wikiRemotePath := WikiRemoteURL(opts.CloneAddr)
|
wikiRemotePath := WikiRemoteURL(opts.CloneAddr)
|
||||||
@@ -87,16 +92,21 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
|
|||||||
return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err)
|
return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = git.CloneWithContext(ctx, wikiRemotePath, wikiPath, git.CloneRepoOptions{
|
if err := git.CloneWithContext(ctx, wikiRemotePath, wikiPath, git.CloneRepoOptions{
|
||||||
Mirror: true,
|
Mirror: true,
|
||||||
Quiet: true,
|
Quiet: true,
|
||||||
Timeout: migrateTimeout,
|
Timeout: migrateTimeout,
|
||||||
Branch: "master",
|
Branch: "master",
|
||||||
|
SkipTLSVerify: setting.Migrations.SkipTLSVerify,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Warn("Clone wiki: %v", err)
|
log.Warn("Clone wiki: %v", err)
|
||||||
if err := util.RemoveAll(wikiPath); err != nil {
|
if err := util.RemoveAll(wikiPath); err != nil {
|
||||||
return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err)
|
return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if err := git.WriteCommitGraph(ctx, wikiPath); err != nil {
|
||||||
|
return repo, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -276,23 +286,25 @@ func SyncReleasesWithTags(repo *repo_model.Repository, gitRepo *git.Repository)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tags, err := gitRepo.GetTags(0, 0)
|
|
||||||
if err != nil {
|
_, err := gitRepo.WalkReferences(git.ObjectTag, 0, 0, func(sha1, refname string) error {
|
||||||
return fmt.Errorf("unable to GetTags in Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err)
|
tagName := strings.TrimPrefix(refname, git.TagPrefix)
|
||||||
}
|
if _, ok := existingRelTags[strings.ToLower(tagName)]; ok {
|
||||||
for _, tagName := range tags {
|
return nil
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
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
|
// PushUpdateAddTag must be called for any push actions to add tag
|
||||||
func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagName string) error {
|
func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagName, sha1, refname string) error {
|
||||||
tag, err := gitRepo.GetTag(tagName)
|
tag, err := gitRepo.GetTagWithID(sha1, tagName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to GetTag: %w", err)
|
return fmt.Errorf("unable to GetTag: %w", err)
|
||||||
}
|
}
|
||||||
@@ -310,7 +322,7 @@ func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagN
|
|||||||
}
|
}
|
||||||
|
|
||||||
var author *user_model.User
|
var author *user_model.User
|
||||||
var createdAt = time.Unix(1, 0)
|
createdAt := time.Unix(1, 0)
|
||||||
|
|
||||||
if sig != nil {
|
if sig != nil {
|
||||||
author, err = user_model.GetUserByEmail(sig.Email)
|
author, err = user_model.GetUserByEmail(sig.Email)
|
||||||
@@ -325,7 +337,7 @@ func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagN
|
|||||||
return fmt.Errorf("unable to get CommitsCount: %w", err)
|
return fmt.Errorf("unable to get CommitsCount: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var rel = models.Release{
|
rel := models.Release{
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
TagName: tagName,
|
TagName: tagName,
|
||||||
LowerTagName: strings.ToLower(tagName),
|
LowerTagName: strings.ToLower(tagName),
|
||||||
|
|||||||
@@ -15,13 +15,17 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"golang.org/x/text/cases"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
ini "gopkg.in/ini.v1"
|
ini "gopkg.in/ini.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var filenameSuffix = ""
|
var (
|
||||||
var descriptionLock = sync.RWMutex{}
|
filenameSuffix = ""
|
||||||
var logDescriptions = make(map[string]*LogDescription)
|
descriptionLock = sync.RWMutex{}
|
||||||
|
logDescriptions = make(map[string]*LogDescription)
|
||||||
|
)
|
||||||
|
|
||||||
// GetLogDescriptions returns a race safe set of descriptions
|
// GetLogDescriptions returns a race safe set of descriptions
|
||||||
func GetLogDescriptions() map[string]*LogDescription {
|
func GetLogDescriptions() map[string]*LogDescription {
|
||||||
@@ -86,7 +90,7 @@ func RemoveSubLogDescription(key, name string) bool {
|
|||||||
type defaultLogOptions struct {
|
type defaultLogOptions struct {
|
||||||
levelName string // LogLevel
|
levelName string // LogLevel
|
||||||
flags string
|
flags string
|
||||||
filename string //path.Join(LogRootPath, "gitea.log")
|
filename string // path.Join(LogRootPath, "gitea.log")
|
||||||
bufferLength int64
|
bufferLength int64
|
||||||
disableConsole bool
|
disableConsole bool
|
||||||
}
|
}
|
||||||
@@ -243,7 +247,7 @@ func generateNamedLogger(key string, options defaultLogOptions) *LogDescription
|
|||||||
Provider: provider,
|
Provider: provider,
|
||||||
Config: config,
|
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)
|
AddLogDescription(key, &description)
|
||||||
@@ -327,7 +331,7 @@ func newLogService() {
|
|||||||
Provider: provider,
|
Provider: provider,
|
||||||
Config: config,
|
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)
|
AddLogDescription(log.DEFAULT, &description)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ package setting
|
|||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -30,6 +29,8 @@ import (
|
|||||||
|
|
||||||
"github.com/unknwon/com"
|
"github.com/unknwon/com"
|
||||||
gossh "golang.org/x/crypto/ssh"
|
gossh "golang.org/x/crypto/ssh"
|
||||||
|
"golang.org/x/text/cases"
|
||||||
|
"golang.org/x/text/language"
|
||||||
ini "gopkg.in/ini.v1"
|
ini "gopkg.in/ini.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -90,13 +91,15 @@ var (
|
|||||||
// AppDataPath is the default path for storing data.
|
// AppDataPath is the default path for storing data.
|
||||||
// It maps to ini:"APP_DATA_PATH" and defaults to AppWorkPath + "/data"
|
// It maps to ini:"APP_DATA_PATH" and defaults to AppWorkPath + "/data"
|
||||||
AppDataPath string
|
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
|
// Server settings
|
||||||
Protocol Scheme
|
Protocol Scheme
|
||||||
Domain string
|
Domain string
|
||||||
HTTPAddr string
|
HTTPAddr string
|
||||||
HTTPPort string
|
HTTPPort string
|
||||||
LocalURL string
|
|
||||||
RedirectOtherPort bool
|
RedirectOtherPort bool
|
||||||
PortToRedirect string
|
PortToRedirect string
|
||||||
OfflineMode bool
|
OfflineMode bool
|
||||||
@@ -637,7 +640,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
|
|||||||
}
|
}
|
||||||
UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666")
|
UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666")
|
||||||
UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32)
|
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)
|
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 = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL)
|
||||||
|
LocalURL = strings.TrimRight(LocalURL, "/") + "/"
|
||||||
RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false)
|
RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false)
|
||||||
PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80")
|
PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80")
|
||||||
OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
|
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(","))
|
SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(","))
|
||||||
|
|
||||||
if !SSH.Disabled && !SSH.StartBuiltinServer {
|
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)
|
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)
|
log.Fatal("Failed to create '%s': %v", SSH.KeyTestPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(trustedUserCaKeys) > 0 && SSH.AuthorizedPrincipalsEnabled {
|
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"))
|
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,
|
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)
|
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.
|
// 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.
|
// 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)
|
unsafeAllowRunAsRoot := Cfg.Section("").Key("I_AM_BEING_UNSAFE_RUNNING_AS_ROOT").MustBool(false)
|
||||||
RunMode = Cfg.Section("").Key("RUN_MODE").MustString("prod")
|
RunMode = Cfg.Section("").Key("RUN_MODE").MustString("Prod")
|
||||||
IsProd = strings.EqualFold(RunMode, "prod")
|
RunMode = cases.Title(language.English).String(strings.ToLower(RunMode))
|
||||||
|
IsProd = RunMode == "Prod"
|
||||||
// Does not check run user when the install lock is off.
|
// Does not check run user when the install lock is off.
|
||||||
if InstallLock {
|
if InstallLock {
|
||||||
currentUser, match := IsRunUserMatchCurrentUser(RunUser)
|
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.ShowUserEmail = Cfg.Section("ui").Key("SHOW_USER_EMAIL").MustBool(true)
|
||||||
UI.DefaultShowFullName = Cfg.Section("ui").Key("DEFAULT_SHOW_FULL_NAME").MustBool(false)
|
UI.DefaultShowFullName = Cfg.Section("ui").Key("DEFAULT_SHOW_FULL_NAME").MustBool(false)
|
||||||
UI.SearchRepoDescription = Cfg.Section("ui").Key("SEARCH_REPO_DESCRIPTION").MustBool(true)
|
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"))
|
HasRobotsTxt, err = util.IsFile(path.Join(CustomPath, "robots.txt"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1074,28 +1079,22 @@ func loadInternalToken(sec *ini.Section) string {
|
|||||||
}
|
}
|
||||||
switch tempURI.Scheme {
|
switch tempURI.Scheme {
|
||||||
case "file":
|
case "file":
|
||||||
fp, err := os.OpenFile(tempURI.RequestURI(), os.O_RDWR, 0600)
|
buf, err := os.ReadFile(tempURI.RequestURI())
|
||||||
if err != nil {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
log.Fatal("Failed to open InternalTokenURI (%s): %v", uri, 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.
|
// No token in the file, generate one and store it.
|
||||||
if len(buf) == 0 {
|
if len(buf) == 0 {
|
||||||
token, err := generate.NewInternalToken()
|
token, err := generate.NewInternalToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error generate internal token: %v", err)
|
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)
|
log.Fatal("Error writing to InternalTokenURI (%s): %v", uri, err)
|
||||||
}
|
}
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.TrimSpace(string(buf))
|
return strings.TrimSpace(string(buf))
|
||||||
default:
|
default:
|
||||||
log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri)
|
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)
|
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.
|
// GenKeyPair make a pair of public and private keys for SSH access.
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ package storage
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@@ -18,8 +17,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/util"
|
"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{}
|
var _ ObjectStorage = &LocalStorage{}
|
||||||
|
|
||||||
// LocalStorageType is the type descriptor for local storage
|
// LocalStorageType is the type descriptor for local storage
|
||||||
@@ -62,21 +59,18 @@ func NewLocalStorage(ctx context.Context, cfg interface{}) (ObjectStorage, error
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *LocalStorage) buildLocalPath(p string) string {
|
||||||
|
return filepath.Join(l.dir, path.Clean("/" + strings.ReplaceAll(p, "\\", "/"))[1:])
|
||||||
|
}
|
||||||
|
|
||||||
// Open a file
|
// Open a file
|
||||||
func (l *LocalStorage) Open(path string) (Object, error) {
|
func (l *LocalStorage) Open(path string) (Object, error) {
|
||||||
if !isLocalPathValid(path) {
|
return os.Open(l.buildLocalPath(path))
|
||||||
return nil, ErrLocalPathNotSupported
|
|
||||||
}
|
|
||||||
return os.Open(filepath.Join(l.dir, path))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save a file
|
// Save a file
|
||||||
func (l *LocalStorage) Save(path string, r io.Reader, size int64) (int64, error) {
|
func (l *LocalStorage) Save(path string, r io.Reader, size int64) (int64, error) {
|
||||||
if !isLocalPathValid(path) {
|
p := l.buildLocalPath(path)
|
||||||
return 0, ErrLocalPathNotSupported
|
|
||||||
}
|
|
||||||
|
|
||||||
p := filepath.Join(l.dir, path)
|
|
||||||
if err := os.MkdirAll(filepath.Dir(p), os.ModePerm); err != nil {
|
if err := os.MkdirAll(filepath.Dir(p), os.ModePerm); err != nil {
|
||||||
return 0, err
|
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
|
// Stat returns the info of the file
|
||||||
func (l *LocalStorage) Stat(path string) (os.FileInfo, error) {
|
func (l *LocalStorage) Stat(path string) (os.FileInfo, error) {
|
||||||
return os.Stat(filepath.Join(l.dir, path))
|
return os.Stat(l.buildLocalPath(path))
|
||||||
}
|
|
||||||
|
|
||||||
func isLocalPathValid(p string) bool {
|
|
||||||
a := path.Clean(p)
|
|
||||||
if strings.HasPrefix(a, "../") || strings.HasPrefix(a, "..\\") {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return a == p
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete delete a file
|
// Delete delete a file
|
||||||
func (l *LocalStorage) Delete(path string) error {
|
func (l *LocalStorage) Delete(path string) error {
|
||||||
if !isLocalPathValid(path) {
|
return util.Remove(l.buildLocalPath(path))
|
||||||
return ErrLocalPathNotSupported
|
|
||||||
}
|
|
||||||
p := filepath.Join(l.dir, path)
|
|
||||||
return util.Remove(p)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// URL gets the redirect URL to a file
|
// URL gets the redirect URL to a file
|
||||||
|
|||||||
@@ -10,36 +10,44 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLocalPathIsValid(t *testing.T) {
|
func TestBuildLocalPath(t *testing.T) {
|
||||||
kases := []struct {
|
kases := []struct {
|
||||||
path string
|
localDir string
|
||||||
valid bool
|
path string
|
||||||
|
expected string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
"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",
|
||||||
false,
|
"../0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||||
|
"a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"a\\0\\a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
"a",
|
||||||
true,
|
"0\\a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||||
|
"a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"b/../a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
"b",
|
||||||
false,
|
"a/../0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||||
|
"b/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"..\\a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
"b",
|
||||||
false,
|
"a\\..\\0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||||
|
"b/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, k := range kases {
|
for _, k := range kases {
|
||||||
t.Run(k.path, func(t *testing.T) {
|
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 {
|
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
|
// Open open a file
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type CreateUserOption struct {
|
|||||||
Password string `json:"password" binding:"Required;MaxSize(255)"`
|
Password string `json:"password" binding:"Required;MaxSize(255)"`
|
||||||
MustChangePassword *bool `json:"must_change_password"`
|
MustChangePassword *bool `json:"must_change_password"`
|
||||||
SendNotify bool `json:"send_notify"`
|
SendNotify bool `json:"send_notify"`
|
||||||
|
Restricted *bool `json:"restricted"`
|
||||||
Visibility string `json:"visibility" binding:"In(,public,limited,private)"`
|
Visibility string `json:"visibility" binding:"In(,public,limited,private)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -183,6 +183,8 @@ type EditRepoOption struct {
|
|||||||
Archived *bool `json:"archived,omitempty"`
|
Archived *bool `json:"archived,omitempty"`
|
||||||
// set to a string like `8h30m0s` to set the mirror interval time
|
// set to a string like `8h30m0s` to set the mirror interval time
|
||||||
MirrorInterval *string `json:"mirror_interval,omitempty"`
|
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
|
// GenerateRepoOption options when creating repository using a template
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/services/gitdiff"
|
"code.gitea.io/gitea/services/gitdiff"
|
||||||
|
"golang.org/x/text/cases"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
"github.com/editorconfig/editorconfig-core-go/v2"
|
"github.com/editorconfig/editorconfig-core-go/v2"
|
||||||
)
|
)
|
||||||
@@ -49,7 +51,7 @@ var mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}[\s]*$`)
|
|||||||
func NewFuncMap() []template.FuncMap {
|
func NewFuncMap() []template.FuncMap {
|
||||||
return []template.FuncMap{map[string]interface{}{
|
return []template.FuncMap{map[string]interface{}{
|
||||||
"GoVer": func() string {
|
"GoVer": func() string {
|
||||||
return strings.Title(runtime.Version())
|
return cases.Title(language.English).String(runtime.Version())
|
||||||
},
|
},
|
||||||
"UseHTTPS": func() bool {
|
"UseHTTPS": func() bool {
|
||||||
return strings.HasPrefix(setting.AppURL, "https")
|
return strings.HasPrefix(setting.AppURL, "https")
|
||||||
@@ -285,7 +287,7 @@ func NewFuncMap() []template.FuncMap {
|
|||||||
return util.MergeInto(dict, values...)
|
return util.MergeInto(dict, values...)
|
||||||
},
|
},
|
||||||
"percentage": func(n int, values ...int) float32 {
|
"percentage": func(n int, values ...int) float32 {
|
||||||
var sum = 0
|
sum := 0
|
||||||
for i := 0; i < len(values); i++ {
|
for i := 0; i < len(values); i++ {
|
||||||
sum += values[i]
|
sum += values[i]
|
||||||
}
|
}
|
||||||
@@ -378,6 +380,7 @@ func NewFuncMap() []template.FuncMap {
|
|||||||
},
|
},
|
||||||
"Join": strings.Join,
|
"Join": strings.Join,
|
||||||
"QueryEscape": url.QueryEscape,
|
"QueryEscape": url.QueryEscape,
|
||||||
|
"DotEscape": DotEscape,
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,7 +389,7 @@ func NewFuncMap() []template.FuncMap {
|
|||||||
func NewTextFuncMap() []texttmpl.FuncMap {
|
func NewTextFuncMap() []texttmpl.FuncMap {
|
||||||
return []texttmpl.FuncMap{map[string]interface{}{
|
return []texttmpl.FuncMap{map[string]interface{}{
|
||||||
"GoVer": func() string {
|
"GoVer": func() string {
|
||||||
return strings.Title(runtime.Version())
|
return cases.Title(language.English).String(runtime.Version())
|
||||||
},
|
},
|
||||||
"AppName": func() string {
|
"AppName": func() string {
|
||||||
return setting.AppName
|
return setting.AppName
|
||||||
@@ -477,7 +480,7 @@ func NewTextFuncMap() []texttmpl.FuncMap {
|
|||||||
return dict, nil
|
return dict, nil
|
||||||
},
|
},
|
||||||
"percentage": func(n int, values ...int) float32 {
|
"percentage": func(n int, values ...int) float32 {
|
||||||
var sum = 0
|
sum := 0
|
||||||
for i := 0; i < len(values); i++ {
|
for i := 0; i < len(values); i++ {
|
||||||
sum += values[i]
|
sum += values[i]
|
||||||
}
|
}
|
||||||
@@ -501,8 +504,10 @@ func NewTextFuncMap() []texttmpl.FuncMap {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
var widthRe = regexp.MustCompile(`width="[0-9]+?"`)
|
var (
|
||||||
var heightRe = regexp.MustCompile(`height="[0-9]+?"`)
|
widthRe = regexp.MustCompile(`width="[0-9]+?"`)
|
||||||
|
heightRe = regexp.MustCompile(`height="[0-9]+?"`)
|
||||||
|
)
|
||||||
|
|
||||||
func parseOthers(defaultSize int, defaultClass string, others ...interface{}) (int, string) {
|
func parseOthers(defaultSize int, defaultClass string, others ...interface{}) (int, string) {
|
||||||
size := defaultSize
|
size := defaultSize
|
||||||
@@ -629,6 +634,11 @@ func JSEscape(raw string) string {
|
|||||||
return template.JSEscapeString(raw)
|
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
|
// Sha1 returns sha1 sum of string
|
||||||
func Sha1(str string) string {
|
func Sha1(str string) string {
|
||||||
return base.EncodeSha1(str)
|
return base.EncodeSha1(str)
|
||||||
@@ -736,7 +746,7 @@ func RenderEmoji(text string) template.HTML {
|
|||||||
return template.HTML(renderedText)
|
return template.HTML(renderedText)
|
||||||
}
|
}
|
||||||
|
|
||||||
//ReactionToEmoji renders emoji for use in reactions
|
// ReactionToEmoji renders emoji for use in reactions
|
||||||
func ReactionToEmoji(reaction string) template.HTML {
|
func ReactionToEmoji(reaction string) template.HTML {
|
||||||
val := emoji.FromCode(reaction)
|
val := emoji.FromCode(reaction)
|
||||||
if val != nil {
|
if val != nil {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ func MockContext(t *testing.T, path string) *context.Context {
|
|||||||
Resp: context.NewResponse(resp),
|
Resp: context.NewResponse(resp),
|
||||||
Locale: &mockLocale{},
|
Locale: &mockLocale{},
|
||||||
}
|
}
|
||||||
|
defer ctx.Close()
|
||||||
|
|
||||||
requestURL, err := url.Parse(path)
|
requestURL, err := url.Parse(path)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"net"
|
"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 {
|
func IsIPPrivate(ip net.IP) bool {
|
||||||
if ip4 := ip.To4(); ip4 != nil {
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
return ip4[0] == 10 ||
|
return ip4[0] == 10 ||
|
||||||
|
|||||||
18
modules/util/slice.go
Normal file
18
modules/util/slice.go
Normal file
@@ -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.manually_merged_as=Запрос на слияние был объединён вручную, как <a rel="nofollow" class="ui sha" href="%[1]s"><code>%[2]s</code></a>.
|
||||||
pulls.is_closed=Запрос на слияние был закрыт.
|
pulls.is_closed=Запрос на слияние был закрыт.
|
||||||
pulls.has_merged=Слияние этого запроса успешно завершено.
|
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.cannot_merge_work_in_progress=Этот запрос на слияние помечен как в процессе работы.
|
||||||
pulls.still_in_progress=Всё ещё в процессе?
|
pulls.still_in_progress=Всё ещё в процессе?
|
||||||
pulls.add_prefix=Добавить <strong>%s</strong> префикс
|
pulls.add_prefix=Добавить <strong>%s</strong> префикс
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/password"
|
"code.gitea.io/gitea/modules/password"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/routers/api/v1/user"
|
"code.gitea.io/gitea/routers/api/v1/user"
|
||||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
@@ -81,7 +82,6 @@ func CreateUser(ctx *context.APIContext) {
|
|||||||
Email: form.Email,
|
Email: form.Email,
|
||||||
Passwd: form.Password,
|
Passwd: form.Password,
|
||||||
MustChangePassword: true,
|
MustChangePassword: true,
|
||||||
IsActive: true,
|
|
||||||
LoginType: auth.Plain,
|
LoginType: auth.Plain,
|
||||||
}
|
}
|
||||||
if form.MustChangePassword != nil {
|
if form.MustChangePassword != nil {
|
||||||
@@ -107,11 +107,17 @@ func CreateUser(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var overwriteDefault *user_model.CreateUserOverwriteOptions
|
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
||||||
|
IsActive: util.OptionalBoolTrue,
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.Restricted != nil {
|
||||||
|
overwriteDefault.IsRestricted = util.OptionalBoolOf(*form.Restricted)
|
||||||
|
}
|
||||||
|
|
||||||
if form.Visibility != "" {
|
if form.Visibility != "" {
|
||||||
overwriteDefault = &user_model.CreateUserOverwriteOptions{
|
visibility := api.VisibilityModes[form.Visibility]
|
||||||
Visibility: api.VisibilityModes[form.Visibility],
|
overwriteDefault.Visibility = &visibility
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := user_model.CreateUser(u, overwriteDefault); err != nil {
|
if err := user_model.CreateUser(u, overwriteDefault); err != nil {
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ func createContext(req *http.Request) (*context.Context, *httptest.ResponseRecor
|
|||||||
Render: rnd,
|
Render: rnd,
|
||||||
Data: make(map[string]interface{}),
|
Data: make(map[string]interface{}),
|
||||||
}
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
return c, resp
|
return c, resp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ func ReadRepoNotifications(ctx *context.APIContext) {
|
|||||||
targetStatus = models.NotificationStatusRead
|
targetStatus = models.NotificationStatusRead
|
||||||
}
|
}
|
||||||
|
|
||||||
changed := make([]*structs.NotificationThread, len(nl))
|
changed := make([]*structs.NotificationThread, 0, len(nl))
|
||||||
|
|
||||||
for _, n := range nl {
|
for _, n := range nl {
|
||||||
notif, err := models.SetNotificationStatus(n.ID, ctx.User, targetStatus)
|
notif, err := models.SetNotificationStatus(n.ID, ctx.User, targetStatus)
|
||||||
|
|||||||
@@ -177,23 +177,18 @@ func CreateBranch(ctx *context.APIContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := repo_service.CreateNewBranch(ctx.User, ctx.Repo.Repository, opt.OldBranchName, opt.BranchName)
|
err := repo_service.CreateNewBranch(ctx.User, ctx.Repo.Repository, opt.OldBranchName, opt.BranchName)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrBranchDoesNotExist(err) {
|
if models.IsErrBranchDoesNotExist(err) {
|
||||||
ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
|
ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
|
||||||
}
|
}
|
||||||
if models.IsErrTagAlreadyExists(err) {
|
if models.IsErrTagAlreadyExists(err) {
|
||||||
ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.")
|
ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.")
|
||||||
|
|
||||||
} else if models.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
|
} else if models.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
|
||||||
ctx.Error(http.StatusConflict, "", "The branch already exists.")
|
ctx.Error(http.StatusConflict, "", "The branch already exists.")
|
||||||
|
|
||||||
} else if models.IsErrBranchNameConflict(err) {
|
} else if models.IsErrBranchNameConflict(err) {
|
||||||
ctx.Error(http.StatusConflict, "", "The branch with the same name already exists.")
|
ctx.Error(http.StatusConflict, "", "The branch with the same name already exists.")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
ctx.Error(http.StatusInternalServerError, "CreateRepoBranch", err)
|
ctx.Error(http.StatusInternalServerError, "CreateRepoBranch", err)
|
||||||
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -263,10 +258,15 @@ func ListBranches(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiBranches := make([]*api.Branch, len(branches))
|
apiBranches := make([]*api.Branch, 0, len(branches))
|
||||||
for i := range branches {
|
for i := range branches {
|
||||||
c, err := branches[i].GetCommit()
|
c, err := branches[i].GetCommit()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Skip if this branch doesn't exist anymore.
|
||||||
|
if git.IsErrNotExist(err) {
|
||||||
|
totalNumOfBranches--
|
||||||
|
continue
|
||||||
|
}
|
||||||
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
|
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -275,11 +275,12 @@ func ListBranches(ctx *context.APIContext) {
|
|||||||
ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
|
ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
apiBranches[i], err = convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.User, ctx.Repo.IsAdmin())
|
apiBranch, err := convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.User, ctx.Repo.IsAdmin())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
apiBranches = append(apiBranches, apiBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.SetLinkHeader(totalNumOfBranches, listOptions.PageSize)
|
ctx.SetLinkHeader(totalNumOfBranches, listOptions.PageSize)
|
||||||
@@ -532,7 +533,6 @@ func CreateBranchProtection(ctx *context.APIContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSON(http.StatusCreated, convert.ToBranchProtection(bp))
|
ctx.JSON(http.StatusCreated, convert.ToBranchProtection(bp))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditBranchProtection edits a branch protection for a repo
|
// EditBranchProtection edits a branch protection for a repo
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ func SearchIssues(ctx *context.APIContext) {
|
|||||||
opts.TeamID = team.ID
|
opts.TeamID = team.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
repoCond := models.SearchRepositoryCondition(opts)
|
||||||
repoIDs, _, err := models.SearchRepositoryIDs(opts)
|
repoIDs, _, err := models.SearchRepositoryIDs(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "SearchRepositoryByName", err)
|
ctx.Error(http.StatusInternalServerError, "SearchRepositoryByName", err)
|
||||||
@@ -233,7 +234,7 @@ func SearchIssues(ctx *context.APIContext) {
|
|||||||
Page: ctx.FormInt("page"),
|
Page: ctx.FormInt("page"),
|
||||||
PageSize: limit,
|
PageSize: limit,
|
||||||
},
|
},
|
||||||
RepoIDs: repoIDs,
|
RepoCond: repoCond,
|
||||||
IsClosed: isClosed,
|
IsClosed: isClosed,
|
||||||
IssueIDs: issueIDs,
|
IssueIDs: issueIDs,
|
||||||
IncludedLabelNames: includedLabelNames,
|
IncludedLabelNames: includedLabelNames,
|
||||||
@@ -245,18 +246,23 @@ func SearchIssues(ctx *context.APIContext) {
|
|||||||
UpdatedAfterUnix: since,
|
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
|
// Filter for: Created by User, Assigned to User, Mentioning User, Review of User Requested
|
||||||
if ctx.FormBool("created") {
|
if ctx.FormBool("created") {
|
||||||
issuesOpt.PosterID = ctx.User.ID
|
issuesOpt.PosterID = ctxUserID
|
||||||
}
|
}
|
||||||
if ctx.FormBool("assigned") {
|
if ctx.FormBool("assigned") {
|
||||||
issuesOpt.AssigneeID = ctx.User.ID
|
issuesOpt.AssigneeID = ctxUserID
|
||||||
}
|
}
|
||||||
if ctx.FormBool("mentioned") {
|
if ctx.FormBool("mentioned") {
|
||||||
issuesOpt.MentionedID = ctx.User.ID
|
issuesOpt.MentionedID = ctxUserID
|
||||||
}
|
}
|
||||||
if ctx.FormBool("review_requested") {
|
if ctx.FormBool("review_requested") {
|
||||||
issuesOpt.ReviewRequestedID = ctx.User.ID
|
issuesOpt.ReviewRequestedID = ctxUserID
|
||||||
}
|
}
|
||||||
|
|
||||||
if issues, err = models.Issues(issuesOpt); err != nil {
|
if issues, err = models.Issues(issuesOpt); err != nil {
|
||||||
@@ -455,7 +461,7 @@ func ListIssues(ctx *context.APIContext) {
|
|||||||
if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 {
|
if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 {
|
||||||
issuesOpt := &models.IssuesOptions{
|
issuesOpt := &models.IssuesOptions{
|
||||||
ListOptions: listOptions,
|
ListOptions: listOptions,
|
||||||
RepoIDs: []int64{ctx.Repo.Repository.ID},
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
IsClosed: isClosed,
|
IsClosed: isClosed,
|
||||||
IssueIDs: issueIDs,
|
IssueIDs: issueIDs,
|
||||||
LabelIDs: labelIDs,
|
LabelIDs: labelIDs,
|
||||||
@@ -599,7 +605,7 @@ func CreateIssue(ctx *context.APIContext) {
|
|||||||
DeadlineUnix: deadlineUnix,
|
DeadlineUnix: deadlineUnix,
|
||||||
}
|
}
|
||||||
|
|
||||||
var assigneeIDs = make([]int64, 0)
|
assigneeIDs := make([]int64, 0)
|
||||||
var err error
|
var err error
|
||||||
if ctx.Repo.CanWrite(unit.TypeIssues) {
|
if ctx.Repo.CanWrite(unit.TypeIssues) {
|
||||||
issue.MilestoneID = form.Milestone
|
issue.MilestoneID = form.Milestone
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ func GetDeployKey(ctx *context.APIContext) {
|
|||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/DeployKey"
|
// "$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 err != nil {
|
||||||
if asymkey_model.IsErrDeployKeyNotExist(err) {
|
if asymkey_model.IsErrDeployKeyNotExist(err) {
|
||||||
ctx.NotFound()
|
ctx.NotFound()
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ func ListPullRequests(ctx *context.APIContext) {
|
|||||||
Labels: ctx.FormStrings("labels"),
|
Labels: ctx.FormStrings("labels"),
|
||||||
MilestoneID: ctx.FormInt64("milestone"),
|
MilestoneID: ctx.FormInt64("milestone"),
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "PullRequests", err)
|
ctx.Error(http.StatusInternalServerError, "PullRequests", err)
|
||||||
return
|
return
|
||||||
@@ -724,13 +723,12 @@ func MergePullRequest(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = pr.LoadHeadRepo(); err != nil {
|
if err := pr.LoadHeadRepo(); err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
|
ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = pr.LoadIssue()
|
if err := pr.LoadIssue(); err != nil {
|
||||||
if err != nil {
|
|
||||||
ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
|
ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -744,29 +742,33 @@ func MergePullRequest(ctx *context.APIContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if pr.Issue.IsClosed {
|
manuallMerge := repo_model.MergeStyle(form.Do) == repo_model.MergeStyleManuallyMerged
|
||||||
ctx.NotFound()
|
force := form.ForceMerge != nil && *form.ForceMerge
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
allowedMerge, err := pull_service.IsUserAllowedToMerge(pr, ctx.Repo.Permission, ctx.User)
|
if err := pull_service.CheckPullMergable(ctx, ctx.User, &ctx.Repo.Permission, pr, manuallMerge, force); err != nil {
|
||||||
if err != nil {
|
if errors.Is(err, pull_service.ErrIsClosed) {
|
||||||
ctx.Error(http.StatusInternalServerError, "IsUSerAllowedToMerge", err)
|
ctx.NotFound()
|
||||||
return
|
} else if errors.Is(err, pull_service.ErrUserNotAllowedToMerge) {
|
||||||
}
|
ctx.Error(http.StatusMethodNotAllowed, "Merge", "User not allowed to merge PR")
|
||||||
if !allowedMerge {
|
} else if errors.Is(err, pull_service.ErrHasMerged) {
|
||||||
ctx.Error(http.StatusMethodNotAllowed, "Merge", "User not allowed to merge PR")
|
ctx.Error(http.StatusMethodNotAllowed, "PR already merged", "")
|
||||||
return
|
} 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) {
|
||||||
if pr.HasMerged {
|
ctx.Error(http.StatusMethodNotAllowed, "PR not in mergeable state", "Please try again later")
|
||||||
ctx.Error(http.StatusMethodNotAllowed, "PR already merged", "")
|
} 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle manually-merged mark
|
// handle manually-merged mark
|
||||||
if repo_model.MergeStyle(form.Do) == repo_model.MergeStyleManuallyMerged {
|
if manuallMerge {
|
||||||
if err = pull_service.MergedManually(pr, ctx.User, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
|
if err := pull_service.MergedManually(pr, ctx.User, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
|
||||||
if models.IsErrInvalidMergeStyle(err) {
|
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)))
|
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
|
return
|
||||||
@@ -782,63 +784,13 @@ func MergePullRequest(ctx *context.APIContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !pr.CanAutoMerge() {
|
// set defaults to propagate needed fields
|
||||||
ctx.Error(http.StatusMethodNotAllowed, "PR not in mergeable state", "Please try again later")
|
if err := form.SetDefaults(pr); err != nil {
|
||||||
|
ctx.ServerError("SetDefaults", fmt.Errorf("SetDefaults: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if pr.IsWorkInProgress() {
|
if err := pull_service.Merge(pr, ctx.User, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, form.MergeTitleField); err != nil {
|
||||||
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 models.IsErrInvalidMergeStyle(err) {
|
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)))
|
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
|
return
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ func Search(ctx *context.APIContext) {
|
|||||||
opts.Collaborate = util.OptionalBoolFalse
|
opts.Collaborate = util.OptionalBoolFalse
|
||||||
}
|
}
|
||||||
|
|
||||||
var mode = ctx.FormString("mode")
|
mode := ctx.FormString("mode")
|
||||||
switch mode {
|
switch mode {
|
||||||
case "source":
|
case "source":
|
||||||
opts.Fork = util.OptionalBoolFalse
|
opts.Fork = util.OptionalBoolFalse
|
||||||
@@ -186,9 +186,9 @@ func Search(ctx *context.APIContext) {
|
|||||||
opts.IsPrivate = util.OptionalBoolOf(ctx.FormBool("is_private"))
|
opts.IsPrivate = util.OptionalBoolOf(ctx.FormBool("is_private"))
|
||||||
}
|
}
|
||||||
|
|
||||||
var sortMode = ctx.FormString("sort")
|
sortMode := ctx.FormString("sort")
|
||||||
if len(sortMode) > 0 {
|
if len(sortMode) > 0 {
|
||||||
var sortOrder = ctx.FormString("order")
|
sortOrder := ctx.FormString("order")
|
||||||
if len(sortOrder) == 0 {
|
if len(sortOrder) == 0 {
|
||||||
sortOrder = "asc"
|
sortOrder = "asc"
|
||||||
}
|
}
|
||||||
@@ -264,7 +264,8 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
|
|||||||
if repo_model.IsErrRepoAlreadyExist(err) {
|
if repo_model.IsErrRepoAlreadyExist(err) {
|
||||||
ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.")
|
ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.")
|
||||||
} else if db.IsErrNameReserved(err) ||
|
} else if db.IsErrNameReserved(err) ||
|
||||||
db.IsErrNamePatternNotAllowed(err) {
|
db.IsErrNamePatternNotAllowed(err) ||
|
||||||
|
models.IsErrIssueLabelTemplateLoad(err) {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||||
} else {
|
} else {
|
||||||
ctx.Error(http.StatusInternalServerError, "CreateRepository", err)
|
ctx.Error(http.StatusInternalServerError, "CreateRepository", err)
|
||||||
@@ -624,7 +625,7 @@ func Edit(ctx *context.APIContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if opts.MirrorInterval != nil {
|
if opts.MirrorInterval != nil {
|
||||||
if err := updateMirrorInterval(ctx, opts); err != nil {
|
if err := updateMirror(ctx, opts); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -949,37 +950,67 @@ func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateMirrorInterval updates the repo's mirror Interval
|
// updateMirror updates a repo's mirror Interval and EnablePrune
|
||||||
func updateMirrorInterval(ctx *context.APIContext, opts api.EditRepoOption) error {
|
func updateMirror(ctx *context.APIContext, opts api.EditRepoOption) error {
|
||||||
repo := ctx.Repo.Repository
|
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 opts.MirrorInterval != nil {
|
||||||
if !repo.IsMirror {
|
|
||||||
err := fmt.Errorf("repo is not a mirror, can not change mirror interval")
|
// MirrorInterval should be a duration
|
||||||
ctx.Error(http.StatusUnprocessableEntity, err.Error(), err)
|
interval, err := time.ParseDuration(*opts.MirrorInterval)
|
||||||
return err
|
|
||||||
}
|
|
||||||
mirror, err := repo_model.GetMirrorByRepoID(repo.ID)
|
|
||||||
if err != nil {
|
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)
|
log.Error("Wrong format for MirrorInternal Sent: %s", err)
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err)
|
ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err)
|
||||||
return 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user