merge main branch
This commit is contained in:
commit
9740a6dbbb
1184 changed files with 14955 additions and 10799 deletions
81
.drone.yml
81
.drone.yml
|
@ -90,7 +90,10 @@ steps:
|
|||
- name: checks-backend
|
||||
image: golang:1.19
|
||||
commands:
|
||||
- curl -sL https://deb.nodesource.com/setup_18.x | bash - && apt-get -qqy install nodejs
|
||||
- make checks-backend
|
||||
environment:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
depends_on: [deps-backend]
|
||||
volumes:
|
||||
- name: deps
|
||||
|
@ -498,6 +501,78 @@ steps:
|
|||
- name: deps
|
||||
path: /go
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: testing-e2e
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
depends_on:
|
||||
- compliance
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- pull_request
|
||||
|
||||
volumes:
|
||||
- name: deps
|
||||
temp: {}
|
||||
|
||||
services:
|
||||
- name: pgsql
|
||||
pull: default
|
||||
image: postgres:10
|
||||
environment:
|
||||
POSTGRES_DB: testgitea-e2e
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_INITDB_ARGS: --encoding=UTF8 --lc-collate='en_US.UTF-8' --lc-ctype='en_US.UTF-8'
|
||||
|
||||
steps:
|
||||
- name: deps-frontend
|
||||
image: node:16
|
||||
pull: always
|
||||
commands:
|
||||
- make deps-frontend
|
||||
|
||||
- name: build-frontend
|
||||
image: node:16
|
||||
commands:
|
||||
- make frontend
|
||||
depends_on: [deps-frontend]
|
||||
|
||||
- name: deps-backend
|
||||
image: golang:1.18
|
||||
pull: always
|
||||
commands:
|
||||
- make deps-backend
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
|
||||
# TODO: We should probably build all dependencies into a test image
|
||||
- name: test-e2e
|
||||
image: mcr.microsoft.com/playwright:v1.23.1-focal
|
||||
commands:
|
||||
- curl -sLO https://go.dev/dl/go1.18.linux-amd64.tar.gz && tar -C /usr/local -xzf go1.18.linux-amd64.tar.gz
|
||||
- groupadd --gid 1001 gitea && useradd -m --gid 1001 --uid 1001 gitea
|
||||
- apt-get -qq update && apt-get -qqy install build-essential
|
||||
- export TEST_PGSQL_SCHEMA=''
|
||||
- ./build/test-env-prepare.sh
|
||||
- su gitea bash -c "export PATH=$PATH:/usr/local/go/bin && timeout -s ABRT 40m make test-e2e-pgsql"
|
||||
environment:
|
||||
GOPROXY: https://goproxy.io
|
||||
GOSUMDB: sum.golang.org
|
||||
USE_REPO_TEST_DIR: 1
|
||||
TEST_PGSQL_DBNAME: 'testgitea-e2e'
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
depends_on: [build-frontend, deps-backend]
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: update_translations
|
||||
|
@ -653,12 +728,13 @@ steps:
|
|||
pull: always
|
||||
commands:
|
||||
# Upgrade to node 18 once https://github.com/techknowlogick/xgo/issues/163 is resolved
|
||||
- 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 -qqy install nodejs
|
||||
- export PATH=$PATH:$GOPATH/bin
|
||||
- make release
|
||||
environment:
|
||||
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||
TAGS: bindata sqlite sqlite_unlock_notify
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
|
@ -773,12 +849,13 @@ steps:
|
|||
pull: always
|
||||
commands:
|
||||
# Upgrade to node 18 once https://github.com/techknowlogick/xgo/issues/163 is resolved
|
||||
- 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 -qqy install nodejs
|
||||
- export PATH=$PATH:$GOPATH/bin
|
||||
- make release
|
||||
environment:
|
||||
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||
TAGS: bindata sqlite sqlite_unlock_notify
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
depends_on: [fetch-tags]
|
||||
volumes:
|
||||
- name: deps
|
||||
|
|
24
.gitignore
vendored
24
.gitignore
vendored
|
@ -63,21 +63,14 @@ cpu.out
|
|||
/indexers
|
||||
/log
|
||||
/public/img/avatar
|
||||
/integrations/gitea-integration-mysql
|
||||
/integrations/gitea-integration-mysql8
|
||||
/integrations/gitea-integration-pgsql
|
||||
/integrations/gitea-integration-sqlite
|
||||
/integrations/gitea-integration-mssql
|
||||
/integrations/indexers-mysql
|
||||
/integrations/indexers-mysql8
|
||||
/integrations/indexers-pgsql
|
||||
/integrations/indexers-sqlite
|
||||
/integrations/indexers-mssql
|
||||
/integrations/sqlite.ini
|
||||
/integrations/mysql.ini
|
||||
/integrations/mysql8.ini
|
||||
/integrations/pgsql.ini
|
||||
/integrations/mssql.ini
|
||||
/tests/integration/gitea-integration-*
|
||||
/tests/integration/indexers-*
|
||||
/tests/e2e/gitea-e2e-*
|
||||
/tests/e2e/indexers-*
|
||||
/tests/e2e/reports
|
||||
/tests/e2e/test-artifacts
|
||||
/tests/e2e/test-snapshots
|
||||
/tests/*.ini
|
||||
/node_modules
|
||||
/yarn.lock
|
||||
/yarn-error.log
|
||||
|
@ -102,6 +95,7 @@ cpu.out
|
|||
!/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
|
||||
/VERSION
|
||||
/.air
|
||||
/.go-licenses
|
||||
|
||||
# Snapcraft
|
||||
snap/.snapcraft/
|
||||
|
|
39
CHANGELOG.md
39
CHANGELOG.md
|
@ -4,6 +4,45 @@ This changelog goes through all the changes that have been made in each release
|
|||
without substantial changes to our git log; to see the highlights of what has
|
||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||
|
||||
## [1.17.1](https://github.com/go-gitea/gitea/releases/tag/1.17.1) - 2022-08-17
|
||||
|
||||
* SECURITY
|
||||
* Correctly escape within tribute.js (#20831) (#20832)
|
||||
* ENHANCEMENTS
|
||||
* Add support for NuGet API keys (#20721) (#20734)
|
||||
* Display project in issue list (#20583)
|
||||
* Add disable download source configuration (#20548) (#20579)
|
||||
* Add username check to doctor (#20140) (#20671)
|
||||
* Enable Wire 2 for Internal SSH Server (#20616) (#20617)
|
||||
* BUGFIXES
|
||||
* Use the total issue count for UI (#20785) (#20827)
|
||||
* Add proxy host into allow list (#20798) (#20819)
|
||||
* Add missing translation for queue flush workers (#20791) (#20792)
|
||||
* Improve comment header for mobile (#20781) (#20789)
|
||||
* Fix git.Init for doctor sub-command (#20782) (#20783)
|
||||
* Check webhooks slice length before calling xorm (#20642) (#20768)
|
||||
* Remove manual rollback for failed generated repositories (#20639) (#20762)
|
||||
* Use correct field name in npm template (#20675) (#20760)
|
||||
* Keep download count on Container tag overwrite (#20728) (#20735)
|
||||
* Fix v220 migration to be compatible for MSSQL 2008 r2 (#20702) (#20707)
|
||||
* Use request timeout for git service rpc (#20689) (#20693)
|
||||
* Send correct NuGet status codes (#20647) (#20677)
|
||||
* Use correct context to get package content (#20673) (#20676)
|
||||
* Fix the JS error "EventSource is not defined" caused by some non-standard browsers (#20584) (#20663)
|
||||
* Add default commit messages to PR for squash merge (#20618) (#20645)
|
||||
* Fix package upload for files >32mb (#20622) (#20635)
|
||||
* Fix the new-line copy-paste for rendered code (#20612)
|
||||
* Clean up and fix clone button script (#20415 & #20600) (#20599)
|
||||
* Fix default merge style (#20564) (#20565)
|
||||
* Add repository condition for issue count (#20454) (#20496)
|
||||
* Make branch icon stand out more (#20726) (#20774)
|
||||
* Fix loading button with invalid form (#20754) (#20759)
|
||||
* Fix SecToTime edge-cases (#20610) (#20611)
|
||||
* Executable check always returns true for windows (#20637) (#20835)
|
||||
* Check issue labels slice length before calling xorm Insert (#20655) (#20836)
|
||||
* Fix owners cannot create organization repos bug (#20841) (#20854)
|
||||
* Prevent 500 is head repo does not have PullRequest unit in IsUserAllowedToUpdate (#20839) (#20848)
|
||||
|
||||
## [1.17.0](https://github.com/go-gitea/gitea/releases/tag/v1.17.0) - 2022-07-30
|
||||
|
||||
* BREAKING
|
||||
|
|
|
@ -86,8 +86,10 @@ Here's how to run the test suite:
|
|||
| | |
|
||||
| :------------------------------------- | :----------------------------------------------- |
|
||||
|``make test[\#TestSpecificName]`` | run unit test |
|
||||
|``make test-sqlite[\#TestSpecificName]``| run [integration](integrations) test for SQLite |
|
||||
|[More details about integrations](integrations/README.md) |
|
||||
|``make test-sqlite[\#TestSpecificName]``| run [integration](tests/integration) test for SQLite |
|
||||
|[More details about integration tests](tests/integration/README.md) |
|
||||
|``make test-e2e-sqlite[\#TestSpecificFileName]``| run [end-to-end](tests/e2e) test for SQLite |
|
||||
|[More details about e2e tests](tests/e2e/README.md) |
|
||||
|
||||
## Vendoring
|
||||
|
||||
|
@ -168,7 +170,7 @@ import (
|
|||
|
||||
To maintain understandable code and avoid circular dependencies it is important to have a good structure of the code. The Gitea code is divided into the following parts:
|
||||
|
||||
- **integration:** Integrations tests
|
||||
- **integration:** Integration tests
|
||||
- **models:** Contains the data structures used by xorm to construct database tables. It also contains supporting functions to query and update the database. Dependencies to other code in Gitea should be avoided although some modules might be needed (for example for logging).
|
||||
- **models/fixtures:** Sample model data used in integration tests.
|
||||
- **models/migrations:** Handling of database migrations between versions. PRs that changes a database structure shall also have a migration step.
|
||||
|
|
|
@ -48,3 +48,4 @@ Gusted <williamzijl7@hotmail.com) (@Gusted)
|
|||
silentcode <silentcode@senga.org> (@silentcodeg)
|
||||
Wim <wim@42.be> (@42wim)
|
||||
xinyu <xinyu@nerv.org.cn> (@penlinux)
|
||||
Jason Song <i@wolfogre.com> (@wolfogre)
|
||||
|
|
252
Makefile
252
Makefile
|
@ -32,8 +32,9 @@ GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.3.1
|
|||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.47.0
|
||||
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
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.0
|
||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.3.0
|
||||
|
||||
DOCKER_IMAGE ?= gitea/gitea
|
||||
DOCKER_TAG ?= latest
|
||||
|
@ -98,7 +99,7 @@ LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(G
|
|||
|
||||
LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64
|
||||
|
||||
GO_PACKAGES ?= $(filter-out code.gitea.io/gitea/models/migrations code.gitea.io/gitea/integrations/migration-test code.gitea.io/gitea/integrations,$(shell $(GO) list ./... | grep -v /vendor/))
|
||||
GO_PACKAGES ?= $(filter-out code.gitea.io/gitea/models/migrations code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/))
|
||||
|
||||
FOMANTIC_WORK_DIR := web_src/fomantic
|
||||
|
||||
|
@ -110,25 +111,38 @@ WEBPACK_DEST_ENTRIES := public/js public/css public/fonts public/img/webpack pub
|
|||
BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
|
||||
BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))
|
||||
|
||||
GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go
|
||||
|
||||
SVG_DEST_DIR := public/img/svg
|
||||
|
||||
AIR_TMP_DIR := .air
|
||||
|
||||
GO_LICENSE_TMP_DIR := .go-licenses
|
||||
GO_LICENSE_FILE := assets/go-licenses.json
|
||||
|
||||
TAGS ?=
|
||||
TAGS_SPLIT := $(subst $(COMMA), ,$(TAGS))
|
||||
TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
|
||||
|
||||
TEST_TAGS ?= sqlite sqlite_unlock_notify
|
||||
|
||||
TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMANTIC_WORK_DIR)/node_modules $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR)
|
||||
TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMANTIC_WORK_DIR)/node_modules $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR) $(GO_LICENSE_TMP_DIR)
|
||||
|
||||
GO_DIRS := cmd integrations models modules routers build services tools
|
||||
GO_DIRS := cmd tests models modules routers build services tools
|
||||
|
||||
GO_SOURCES := $(wildcard *.go)
|
||||
GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" -not -path modules/options/bindata.go -not -path modules/public/bindata.go -not -path modules/templates/bindata.go)
|
||||
GO_SOURCES += $(GENERATED_GO_DEST)
|
||||
GO_SOURCES_NO_BINDATA := $(GO_SOURCES)
|
||||
|
||||
ifeq ($(filter $(TAGS_SPLIT),bindata),bindata)
|
||||
GO_SOURCES += $(BINDATA_DEST)
|
||||
GENERATED_GO_DEST += $(BINDATA_DEST)
|
||||
endif
|
||||
|
||||
# Force installation of playwright dependencies by setting this flag
|
||||
ifdef DEPS_PLAYWRIGHT
|
||||
PLAYWRIGHT_FLAGS += --with-deps
|
||||
endif
|
||||
|
||||
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
|
||||
|
@ -182,6 +196,7 @@ help:
|
|||
@echo " - test test everything"
|
||||
@echo " - test-frontend test frontend files"
|
||||
@echo " - test-backend test backend files"
|
||||
@echo " - test-e2e[\#TestSpecificName] test end to end using playwright"
|
||||
@echo " - webpack build webpack files"
|
||||
@echo " - svg build svg files"
|
||||
@echo " - fomantic build fomantic files"
|
||||
|
@ -193,8 +208,9 @@ help:
|
|||
@echo " - generate-swagger generate the swagger spec from code comments"
|
||||
@echo " - swagger-validate check if the swagger spec is valid"
|
||||
@echo " - golangci-lint run golangci-lint linter"
|
||||
@echo " - go-licenses regenerate go licenses"
|
||||
@echo " - vet examines Go source code and reports suspicious constructs"
|
||||
@echo " - tidy run go mod tidy"
|
||||
@echo " - tidy run go mod tidy and regenerate go licenses"
|
||||
@echo " - test[\#TestSpecificName] run unit test"
|
||||
@echo " - test-sqlite[\#TestSpecificName] run integration test for sqlite"
|
||||
@echo " - pr#<index> build and start gitea from a PR with integration test data loaded"
|
||||
|
@ -236,14 +252,20 @@ clean:
|
|||
$(GO) clean -i ./...
|
||||
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) $(BINDATA_HASH) \
|
||||
integrations*.test \
|
||||
integrations/gitea-integration-pgsql/ integrations/gitea-integration-mysql/ integrations/gitea-integration-mysql8/ integrations/gitea-integration-sqlite/ \
|
||||
integrations/gitea-integration-mssql/ integrations/indexers-mysql/ integrations/indexers-mysql8/ integrations/indexers-pgsql integrations/indexers-sqlite \
|
||||
integrations/indexers-mssql integrations/mysql.ini integrations/mysql8.ini integrations/pgsql.ini integrations/mssql.ini man/
|
||||
e2e*.test \
|
||||
tests/integration/gitea-integration-pgsql/ tests/integration/gitea-integration-mysql/ tests/integration/gitea-integration-mysql8/ tests/integration/gitea-integration-sqlite/ \
|
||||
tests/integration/gitea-integration-mssql/ tests/integration/indexers-mysql/ tests/integration/indexers-mysql8/ tests/integration/indexers-pgsql tests/integration/indexers-sqlite \
|
||||
tests/integration/indexers-mssql tests/mysql.ini tests/mysql8.ini tests/pgsql.ini tests/mssql.ini man/ \
|
||||
tests/e2e/gitea-e2e-pgsql/ tests/e2e/gitea-e2e-mysql/ tests/e2e/gitea-e2e-mysql8/ tests/e2e/gitea-e2e-sqlite/ \
|
||||
tests/e2e/gitea-e2e-mssql/ tests/e2e/indexers-mysql/ tests/e2e/indexers-mysql8/ tests/e2e/indexers-pgsql/ tests/e2e/indexers-sqlite/ \
|
||||
tests/e2e/indexers-mssql/ tests/e2e/reports/ tests/e2e/test-artifacts/ tests/e2e/test-snapshots/
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
@echo "Running gitea-fmt (with gofumpt)..."
|
||||
@MISSPELL_PACKAGE=$(MISSPELL_PACKAGE) GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
|
||||
$(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl'))
|
||||
@# strip whitespace after '{{' and before `}}` unless there is only whitespace before it
|
||||
@$(SED_INPLACE) -e 's/{{[ ]\{1,\}/{{/g' -e '/^[ ]\{1,\}}}/! s/[ ]\{1,\}}}/}}/g' $(TEMPLATES)
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
|
@ -261,7 +283,9 @@ TAGS_PREREQ := $(TAGS_EVIDENCE)
|
|||
endif
|
||||
|
||||
.PHONY: generate-swagger
|
||||
generate-swagger:
|
||||
generate-swagger: $(SWAGGER_SPEC)
|
||||
|
||||
$(SWAGGER_SPEC): $(GO_SOURCES_NO_BINDATA)
|
||||
$(GO) run $(SWAGGER_PACKAGE) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
|
||||
|
@ -288,13 +312,19 @@ errcheck:
|
|||
|
||||
.PHONY: 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=$$(MISSPELL_PACKAGE=$(MISSPELL_PACKAGE) GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -l '{file-list}'); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make fmt' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@diff2=$$(git diff templates); \
|
||||
if [ -n "$$diff2" ]; then \
|
||||
echo "Please run 'make fmt' and commit the result:"; \
|
||||
echo "$${diff2}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: checks
|
||||
checks: checks-frontend checks-backend
|
||||
|
@ -303,14 +333,14 @@ checks: checks-frontend checks-backend
|
|||
checks-frontend: lockfile-check svg-check
|
||||
|
||||
.PHONY: checks-backend
|
||||
checks-backend: gomod-check swagger-check swagger-validate
|
||||
checks-backend: tidy-check swagger-check swagger-validate
|
||||
|
||||
.PHONY: lint
|
||||
lint: lint-frontend lint-backend
|
||||
|
||||
.PHONY: lint-frontend
|
||||
lint-frontend: node_modules
|
||||
npx eslint --color --max-warnings=0 --ext js,vue web_src/js build *.config.js docs/assets/js
|
||||
npx eslint --color --max-warnings=0 --ext js,vue web_src/js build *.config.js docs/assets/js tests/e2e/*.test.e2e.js tests/e2e/utils_e2e.js
|
||||
npx stylelint --color --max-warnings=0 web_src/less
|
||||
npx spectral lint -q -F hint $(SWAGGER_SPEC)
|
||||
npx markdownlint docs *.md
|
||||
|
@ -380,35 +410,45 @@ tidy:
|
|||
vendor: tidy
|
||||
$(GO) mod vendor
|
||||
|
||||
.PHONY: gomod-check
|
||||
gomod-check: tidy
|
||||
@diff=$$(git diff go.sum); \
|
||||
.PHONY: tidy-check
|
||||
tidy-check: tidy
|
||||
@diff=$$(git diff go.mod go.sum); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make tidy' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: go-licenses
|
||||
go-licenses: assets/go-licenses.json
|
||||
|
||||
assets/go-licenses.json: go.mod go.sum build/generate-go-licenses.js
|
||||
-$(GO) run $(GO_LICENSES_PACKAGE) save . --force --save_path="$(GO_LICENSE_TMP_DIR)" 2>/dev/null
|
||||
node build/generate-go-licenses.js "$(GO_LICENSE_TMP_DIR)" "$(GO_LICENSE_FILE)"
|
||||
@rm -rf "$(GO_LICENSE_TMP_DIR)"
|
||||
|
||||
generate-ini-sqlite:
|
||||
sed -e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||
integrations/sqlite.ini.tmpl > integrations/sqlite.ini
|
||||
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
||||
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
|
||||
tests/sqlite.ini.tmpl > tests/sqlite.ini
|
||||
|
||||
.PHONY: test-sqlite
|
||||
test-sqlite: integrations.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./integrations.sqlite.test
|
||||
|
||||
.PHONY: test-sqlite\#%
|
||||
test-sqlite\#%: integrations.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.run $(subst .,/,$*)
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./integrations.sqlite.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-sqlite-migration
|
||||
test-sqlite-migration: migrations.sqlite.test migrations.individual.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./migrations.sqlite.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./migrations.individual.sqlite.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./migrations.sqlite.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./migrations.individual.sqlite.test
|
||||
|
||||
.PHONY: test-sqlite-migration\#%
|
||||
test-sqlite-migration\#%: migrations.sqlite.test migrations.individual.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./migrations.individual.sqlite.test -test.run $(subst .,/,$*)
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./migrations.individual.sqlite.test -test.run $(subst .,/,$*)
|
||||
|
||||
|
||||
generate-ini-mysql:
|
||||
|
@ -417,20 +457,22 @@ generate-ini-mysql:
|
|||
-e 's|{{TEST_MYSQL_USERNAME}}|${TEST_MYSQL_USERNAME}|g' \
|
||||
-e 's|{{TEST_MYSQL_PASSWORD}}|${TEST_MYSQL_PASSWORD}|g' \
|
||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||
integrations/mysql.ini.tmpl > integrations/mysql.ini
|
||||
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
||||
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
|
||||
tests/mysql.ini.tmpl > tests/mysql.ini
|
||||
|
||||
.PHONY: test-mysql
|
||||
test-mysql: integrations.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./integrations.mysql.test
|
||||
|
||||
.PHONY: test-mysql\#%
|
||||
test-mysql\#%: integrations.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test -test.run $(subst .,/,$*)
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./integrations.mysql.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-mysql-migration
|
||||
test-mysql-migration: migrations.mysql.test migrations.individual.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./migrations.mysql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./migrations.individual.mysql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./migrations.mysql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./migrations.individual.mysql.test
|
||||
|
||||
generate-ini-mysql8:
|
||||
sed -e 's|{{TEST_MYSQL8_HOST}}|${TEST_MYSQL8_HOST}|g' \
|
||||
|
@ -438,20 +480,22 @@ generate-ini-mysql8:
|
|||
-e 's|{{TEST_MYSQL8_USERNAME}}|${TEST_MYSQL8_USERNAME}|g' \
|
||||
-e 's|{{TEST_MYSQL8_PASSWORD}}|${TEST_MYSQL8_PASSWORD}|g' \
|
||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||
integrations/mysql8.ini.tmpl > integrations/mysql8.ini
|
||||
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
||||
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
|
||||
tests/mysql8.ini.tmpl > tests/mysql8.ini
|
||||
|
||||
.PHONY: test-mysql8
|
||||
test-mysql8: integrations.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./integrations.mysql8.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql8.ini ./integrations.mysql8.test
|
||||
|
||||
.PHONY: test-mysql8\#%
|
||||
test-mysql8\#%: integrations.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./integrations.mysql8.test -test.run $(subst .,/,$*)
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql8.ini ./integrations.mysql8.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-mysql8-migration
|
||||
test-mysql8-migration: migrations.mysql8.test migrations.individual.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./migrations.mysql8.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./migrations.individual.mysql8.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql8.ini ./migrations.mysql8.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql8.ini ./migrations.individual.mysql8.test
|
||||
|
||||
generate-ini-pgsql:
|
||||
sed -e 's|{{TEST_PGSQL_HOST}}|${TEST_PGSQL_HOST}|g' \
|
||||
|
@ -460,20 +504,22 @@ generate-ini-pgsql:
|
|||
-e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \
|
||||
-e 's|{{TEST_PGSQL_SCHEMA}}|${TEST_PGSQL_SCHEMA}|g' \
|
||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||
integrations/pgsql.ini.tmpl > integrations/pgsql.ini
|
||||
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
||||
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
|
||||
tests/pgsql.ini.tmpl > tests/pgsql.ini
|
||||
|
||||
.PHONY: test-pgsql
|
||||
test-pgsql: integrations.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./integrations.pgsql.test
|
||||
|
||||
.PHONY: test-pgsql\#%
|
||||
test-pgsql\#%: integrations.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test -test.run $(subst .,/,$*)
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./integrations.pgsql.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-pgsql-migration
|
||||
test-pgsql-migration: migrations.pgsql.test migrations.individual.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./migrations.pgsql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./migrations.individual.pgsql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./migrations.pgsql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./migrations.individual.pgsql.test
|
||||
|
||||
generate-ini-mssql:
|
||||
sed -e 's|{{TEST_MSSQL_HOST}}|${TEST_MSSQL_HOST}|g' \
|
||||
|
@ -481,85 +527,140 @@ generate-ini-mssql:
|
|||
-e 's|{{TEST_MSSQL_USERNAME}}|${TEST_MSSQL_USERNAME}|g' \
|
||||
-e 's|{{TEST_MSSQL_PASSWORD}}|${TEST_MSSQL_PASSWORD}|g' \
|
||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||
integrations/mssql.ini.tmpl > integrations/mssql.ini
|
||||
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
||||
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
|
||||
tests/mssql.ini.tmpl > tests/mssql.ini
|
||||
|
||||
.PHONY: test-mssql
|
||||
test-mssql: integrations.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./integrations.mssql.test
|
||||
|
||||
.PHONY: test-mssql\#%
|
||||
test-mssql\#%: integrations.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test -test.run $(subst .,/,$*)
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./integrations.mssql.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-mssql-migration
|
||||
test-mssql-migration: migrations.mssql.test migrations.individual.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./migrations.mssql.test -test.failfast
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./migrations.individual.mssql.test -test.failfast
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./migrations.mssql.test -test.failfast
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./migrations.individual.mssql.test -test.failfast
|
||||
|
||||
.PHONY: playwright
|
||||
playwright: $(PLAYWRIGHT_DIR)
|
||||
npm install --no-save @playwright/test
|
||||
npx playwright install $(PLAYWRIGHT_FLAGS)
|
||||
|
||||
.PHONY: test-e2e%
|
||||
test-e2e%: TEST_TYPE ?= e2e
|
||||
# Clear display env variable. Otherwise, chromium tests can fail.
|
||||
DISPLAY=
|
||||
|
||||
.PHONY: test-e2e
|
||||
test-e2e: test-e2e-sqlite
|
||||
|
||||
.PHONY: test-e2e-sqlite
|
||||
test-e2e-sqlite: playwright e2e.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./e2e.sqlite.test
|
||||
|
||||
.PHONY: test-e2e-sqlite\#%
|
||||
test-e2e-sqlite\#%: playwright e2e.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./e2e.sqlite.test -test.run TestE2e/$*
|
||||
|
||||
.PHONY: test-e2e-mysql
|
||||
test-e2e-mysql: playwright e2e.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./e2e.mysql.test
|
||||
|
||||
.PHONY: test-e2e-mysql\#%
|
||||
test-e2e-mysql\#%: playwright e2e.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./e2e.mysql.test -test.run TestE2e/$*
|
||||
|
||||
.PHONY: test-e2e-mysql8
|
||||
test-e2e-mysql8: playwright e2e.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql8.ini ./e2e.mysql8.test
|
||||
|
||||
.PHONY: test-e2e-mysql8\#%
|
||||
test-e2e-mysql8\#%: playwright e2e.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql8.ini ./e2e.mysql8.test -test.run TestE2e/$*
|
||||
|
||||
.PHONY: test-e2e-pgsql
|
||||
test-e2e-pgsql: playwright e2e.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./e2e.pgsql.test
|
||||
|
||||
.PHONY: test-e2e-pgsql\#%
|
||||
test-e2e-pgsql\#%: playwright e2e.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./e2e.pgsql.test -test.run TestE2e/$*
|
||||
|
||||
.PHONY: test-e2e-mssql
|
||||
test-e2e-mssql: playwright e2e.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./e2e.mssql.test
|
||||
|
||||
.PHONY: test-e2e-mssql\#%
|
||||
test-e2e-mssql\#%: playwright e2e.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./e2e.mssql.test -test.run TestE2e/$*
|
||||
|
||||
.PHONY: bench-sqlite
|
||||
bench-sqlite: integrations.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./integrations.sqlite.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
.PHONY: bench-mysql
|
||||
bench-mysql: integrations.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./integrations.mysql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
.PHONY: bench-mssql
|
||||
bench-mssql: integrations.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./integrations.mssql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
.PHONY: bench-pgsql
|
||||
bench-pgsql: integrations.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./integrations.pgsql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
.PHONY: integration-test-coverage
|
||||
integration-test-coverage: integrations.cover.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.cover.test -test.coverprofile=integration.coverage.out
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./integrations.cover.test -test.coverprofile=integration.coverage.out
|
||||
|
||||
.PHONY: integration-test-coverage-sqlite
|
||||
integration-test-coverage-sqlite: integrations.cover.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./integrations.cover.sqlite.test -test.coverprofile=integration.coverage.out
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./integrations.cover.sqlite.test -test.coverprofile=integration.coverage.out
|
||||
|
||||
integrations.mysql.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -o integrations.mysql.test
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.mysql.test
|
||||
|
||||
integrations.mysql8.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -o integrations.mysql8.test
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.mysql8.test
|
||||
|
||||
integrations.pgsql.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -o integrations.pgsql.test
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.pgsql.test
|
||||
|
||||
integrations.mssql.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -o integrations.mssql.test
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.mssql.test
|
||||
|
||||
integrations.sqlite.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -o integrations.sqlite.test -tags '$(TEST_TAGS)'
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.sqlite.test -tags '$(TEST_TAGS)'
|
||||
|
||||
integrations.cover.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -coverpkg $(shell echo $(GO_PACKAGES) | tr ' ' ',') -o integrations.cover.test
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -coverpkg $(shell echo $(GO_PACKAGES) | tr ' ' ',') -o integrations.cover.test
|
||||
|
||||
integrations.cover.sqlite.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -coverpkg $(shell echo $(GO_PACKAGES) | tr ' ' ',') -o integrations.cover.sqlite.test -tags '$(TEST_TAGS)'
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -coverpkg $(shell echo $(GO_PACKAGES) | tr ' ' ',') -o integrations.cover.sqlite.test -tags '$(TEST_TAGS)'
|
||||
|
||||
.PHONY: migrations.mysql.test
|
||||
migrations.mysql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.mysql.test
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.mysql.test
|
||||
|
||||
.PHONY: migrations.mysql8.test
|
||||
migrations.mysql8.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.mysql8.test
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.mysql8.test
|
||||
|
||||
.PHONY: migrations.pgsql.test
|
||||
migrations.pgsql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.pgsql.test
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.pgsql.test
|
||||
|
||||
.PHONY: migrations.mssql.test
|
||||
migrations.mssql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.mssql.test
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.mssql.test
|
||||
|
||||
.PHONY: migrations.sqlite.test
|
||||
migrations.sqlite.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.sqlite.test -tags '$(TEST_TAGS)'
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.sqlite.test -tags '$(TEST_TAGS)'
|
||||
|
||||
.PHONY: migrations.individual.mysql.test
|
||||
migrations.individual.mysql.test: $(GO_SOURCES)
|
||||
|
@ -581,6 +682,21 @@ migrations.individual.mssql.test: $(GO_SOURCES)
|
|||
migrations.individual.sqlite.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/models/migrations -o migrations.individual.sqlite.test -tags '$(TEST_TAGS)'
|
||||
|
||||
e2e.mysql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.mysql.test
|
||||
|
||||
e2e.mysql8.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.mysql8.test
|
||||
|
||||
e2e.pgsql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.pgsql.test
|
||||
|
||||
e2e.mssql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.mssql.test
|
||||
|
||||
e2e.sqlite.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.sqlite.test -tags '$(TEST_TAGS)'
|
||||
|
||||
.PHONY: check
|
||||
check: test
|
||||
|
||||
|
@ -592,16 +708,25 @@ install: $(wildcard *.go)
|
|||
build: frontend backend
|
||||
|
||||
.PHONY: frontend
|
||||
frontend: $(WEBPACK_DEST)
|
||||
frontend: generate-frontend $(WEBPACK_DEST)
|
||||
|
||||
.PHONY: backend
|
||||
backend: go-check generate $(EXECUTABLE)
|
||||
backend: go-check generate-backend $(EXECUTABLE)
|
||||
|
||||
# We generate the backend before the frontend in case we in future we want to generate things in the frontend from generated files in backend
|
||||
.PHONY: generate
|
||||
generate: $(TAGS_PREREQ)
|
||||
generate: generate-backend generate-frontend
|
||||
|
||||
.PHONY: generate-backend
|
||||
generate-backend: $(TAGS_PREREQ) generate-go
|
||||
|
||||
generate-go: $(TAGS_PREREQ)
|
||||
@echo "Running go generate..."
|
||||
@CC= GOOS= GOARCH= $(GO) generate -tags '$(TAGS)' $(GO_PACKAGES)
|
||||
|
||||
.PHONY: generate-frontend
|
||||
generate-frontend: $(TAGS_PREREQ) go-licenses
|
||||
|
||||
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||
|
||||
|
@ -686,6 +811,7 @@ deps-backend:
|
|||
$(GO) install $(MISSPELL_PACKAGE)
|
||||
$(GO) install $(SWAGGER_PACKAGE)
|
||||
$(GO) install $(XGO_PACKAGE)
|
||||
$(GO) install $(GO_LICENSES_PACKAGE)
|
||||
|
||||
node_modules: package-lock.json
|
||||
npm install --no-save
|
||||
|
|
File diff suppressed because one or more lines are too long
762
assets/go-licenses.json
Normal file
762
assets/go-licenses.json
Normal file
File diff suppressed because one or more lines are too long
|
@ -61,7 +61,7 @@ func newFileCollector(fileFilter string, batchSize int) (*fileCollector, error)
|
|||
"build",
|
||||
"cmd",
|
||||
"contrib",
|
||||
"integrations",
|
||||
"tests",
|
||||
"models",
|
||||
"modules",
|
||||
"routers",
|
||||
|
@ -71,8 +71,8 @@ func newFileCollector(fileFilter string, batchSize int) (*fileCollector, error)
|
|||
co.includePatterns = append(co.includePatterns, regexp.MustCompile(`.*\.go$`))
|
||||
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`.*\bbindata\.go$`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`integrations/gitea-repositories-meta`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`integrations/migration-test`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/gitea-repositories-meta`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/integration/migration-test`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`modules/git/tests`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`models/fixtures`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`models/migrations/fixtures`))
|
||||
|
|
|
@ -214,8 +214,7 @@ const hdr = `
|
|||
|
||||
package emoji
|
||||
|
||||
// Code generated by gen.go. DO NOT EDIT.
|
||||
// Code generated by build/generate-emoji.go. DO NOT EDIT.
|
||||
// Sourced from %s
|
||||
//
|
||||
var GemojiData = %#v
|
||||
`
|
||||
|
|
30
build/generate-go-licenses.js
Normal file
30
build/generate-go-licenses.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env node
|
||||
import fastGlob from 'fast-glob';
|
||||
import {fileURLToPath} from 'url';
|
||||
import {readFileSync, writeFileSync} from 'fs';
|
||||
import wrapAnsi from 'wrap-ansi';
|
||||
import {join, dirname} from 'path';
|
||||
|
||||
const base = process.argv[2];
|
||||
const out = process.argv[3];
|
||||
|
||||
function exit(err) {
|
||||
if (err) console.error(err);
|
||||
process.exit(err ? 1 : 0);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const data = fastGlob.sync('**/*', {
|
||||
cwd: fileURLToPath(new URL(`../${base}`, import.meta.url)),
|
||||
}).filter((path) => {
|
||||
return /\/((UN)?LICEN(S|C)E|COPYING|NOTICE)/i.test(path);
|
||||
}).sort().map((path) => {
|
||||
return {
|
||||
name: dirname(path),
|
||||
body: wrapAnsi(readFileSync(join(base, path), 'utf8') || '', 80)
|
||||
};
|
||||
});
|
||||
writeFileSync(out, JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
main().then(exit).catch(exit);
|
|
@ -39,6 +39,14 @@ func main() {
|
|||
|
||||
defer util.Remove(file.Name())
|
||||
|
||||
if err := os.RemoveAll(destination); err != nil {
|
||||
log.Fatalf("Cannot clean destination folder: %v", err)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(destination, 0o755); err != nil {
|
||||
log.Fatalf("Cannot create destination: %v", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to download archive. %s", err)
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"code.gitea.io/gitea/build/codeformat"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) <= 1 {
|
||||
log.Fatalf("Usage: gitea-format-imports [files...]")
|
||||
}
|
||||
|
||||
for _, file := range os.Args[1:] {
|
||||
if err := codeformat.FormatGoImports(file); err != nil {
|
||||
log.Fatalf("can not format file %s, err=%v", file, err)
|
||||
}
|
||||
}
|
||||
}
|
35
cmd/admin.go
35
cmd/admin.go
|
@ -13,9 +13,8 @@ import (
|
|||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
@ -593,12 +592,12 @@ func runCreateUser(c *cli.Context) error {
|
|||
}
|
||||
|
||||
if c.Bool("access-token") {
|
||||
t := &models.AccessToken{
|
||||
t := &auth_model.AccessToken{
|
||||
Name: "gitea-admin",
|
||||
UID: u.ID,
|
||||
}
|
||||
|
||||
if err := models.NewAccessToken(t); err != nil {
|
||||
if err := auth_model.NewAccessToken(t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -700,12 +699,12 @@ func runGenerateAccessToken(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
t := &models.AccessToken{
|
||||
t := &auth_model.AccessToken{
|
||||
Name: c.String("token-name"),
|
||||
UID: user.ID,
|
||||
}
|
||||
|
||||
if err := models.NewAccessToken(t); err != nil {
|
||||
if err := auth_model.NewAccessToken(t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -779,9 +778,9 @@ func runRepoSyncReleases(_ *cli.Context) error {
|
|||
}
|
||||
|
||||
func getReleaseCount(id int64) (int64, error) {
|
||||
return models.GetReleaseCountByRepoID(
|
||||
return repo_model.GetReleaseCountByRepoID(
|
||||
id,
|
||||
models.FindReleasesOptions{
|
||||
repo_model.FindReleasesOptions{
|
||||
IncludeTags: true,
|
||||
},
|
||||
)
|
||||
|
@ -844,8 +843,8 @@ func runAddOauth(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
return auth.CreateSource(&auth.Source{
|
||||
Type: auth.OAuth2,
|
||||
return auth_model.CreateSource(&auth_model.Source{
|
||||
Type: auth_model.OAuth2,
|
||||
Name: c.String("name"),
|
||||
IsActive: true,
|
||||
Cfg: parseOAuth2Config(c),
|
||||
|
@ -864,7 +863,7 @@ func runUpdateOauth(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
source, err := auth.GetSourceByID(c.Int64("id"))
|
||||
source, err := auth_model.GetSourceByID(c.Int64("id"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -944,7 +943,7 @@ func runUpdateOauth(c *cli.Context) error {
|
|||
oAuth2Config.CustomURLMapping = customURLMapping
|
||||
source.Cfg = oAuth2Config
|
||||
|
||||
return auth.UpdateSource(source)
|
||||
return auth_model.UpdateSource(source)
|
||||
}
|
||||
|
||||
func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
|
||||
|
@ -1015,8 +1014,8 @@ func runAddSMTP(c *cli.Context) error {
|
|||
smtpConfig.Auth = "PLAIN"
|
||||
}
|
||||
|
||||
return auth.CreateSource(&auth.Source{
|
||||
Type: auth.SMTP,
|
||||
return auth_model.CreateSource(&auth_model.Source{
|
||||
Type: auth_model.SMTP,
|
||||
Name: c.String("name"),
|
||||
IsActive: active,
|
||||
Cfg: &smtpConfig,
|
||||
|
@ -1035,7 +1034,7 @@ func runUpdateSMTP(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
source, err := auth.GetSourceByID(c.Int64("id"))
|
||||
source, err := auth_model.GetSourceByID(c.Int64("id"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1056,7 +1055,7 @@ func runUpdateSMTP(c *cli.Context) error {
|
|||
|
||||
source.Cfg = smtpConfig
|
||||
|
||||
return auth.UpdateSource(source)
|
||||
return auth_model.UpdateSource(source)
|
||||
}
|
||||
|
||||
func runListAuth(c *cli.Context) error {
|
||||
|
@ -1067,7 +1066,7 @@ func runListAuth(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
authSources, err := auth.Sources()
|
||||
authSources, err := auth_model.Sources()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1105,7 +1104,7 @@ func runDeleteAuth(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
source, err := auth.GetSourceByID(c.Int64("id"))
|
||||
source, err := auth_model.GetSourceByID(c.Int64("id"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
golog "log"
|
||||
"os"
|
||||
|
@ -123,6 +124,47 @@ func runRecreateTable(ctx *cli.Context) error {
|
|||
})
|
||||
}
|
||||
|
||||
func setDoctorLogger(ctx *cli.Context) {
|
||||
logFile := ctx.String("log-file")
|
||||
if !ctx.IsSet("log-file") {
|
||||
logFile = "doctor.log"
|
||||
}
|
||||
colorize := log.CanColorStdout
|
||||
if ctx.IsSet("color") {
|
||||
colorize = ctx.Bool("color")
|
||||
}
|
||||
|
||||
if len(logFile) == 0 {
|
||||
log.NewLogger(1000, "doctor", "console", fmt.Sprintf(`{"level":"NONE","stacktracelevel":"NONE","colorize":%t}`, colorize))
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
recovered := recover()
|
||||
if recovered == nil {
|
||||
return
|
||||
}
|
||||
|
||||
err, ok := recovered.(error)
|
||||
if !ok {
|
||||
panic(recovered)
|
||||
}
|
||||
if errors.Is(err, os.ErrPermission) {
|
||||
fmt.Fprintf(os.Stderr, "ERROR: Unable to write logs to provided file due to permissions error: %s\n %v\n", logFile, err)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "ERROR: Unable to write logs to provided file: %s\n %v\n", logFile, err)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "WARN: Logging will be disabled\n Use `--log-file` to configure log file location\n")
|
||||
log.NewLogger(1000, "doctor", "console", fmt.Sprintf(`{"level":"NONE","stacktracelevel":"NONE","colorize":%t}`, colorize))
|
||||
}()
|
||||
|
||||
if logFile == "-" {
|
||||
log.NewLogger(1000, "doctor", "console", fmt.Sprintf(`{"level":"trace","stacktracelevel":"NONE","colorize":%t}`, colorize))
|
||||
} else {
|
||||
log.NewLogger(1000, "doctor", "file", fmt.Sprintf(`{"filename":%q,"level":"trace","stacktracelevel":"NONE"}`, logFile))
|
||||
}
|
||||
}
|
||||
|
||||
func runDoctor(ctx *cli.Context) error {
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
@ -132,24 +174,13 @@ func runDoctor(ctx *cli.Context) error {
|
|||
log.DelNamedLogger(log.DEFAULT)
|
||||
|
||||
// Now setup our own
|
||||
logFile := ctx.String("log-file")
|
||||
if !ctx.IsSet("log-file") {
|
||||
logFile = "doctor.log"
|
||||
}
|
||||
setDoctorLogger(ctx)
|
||||
|
||||
colorize := log.CanColorStdout
|
||||
if ctx.IsSet("color") {
|
||||
colorize = ctx.Bool("color")
|
||||
}
|
||||
|
||||
if len(logFile) == 0 {
|
||||
log.NewLogger(1000, "doctor", "console", fmt.Sprintf(`{"level":"NONE","stacktracelevel":"NONE","colorize":%t}`, colorize))
|
||||
} else if logFile == "-" {
|
||||
log.NewLogger(1000, "doctor", "console", fmt.Sprintf(`{"level":"trace","stacktracelevel":"NONE","colorize":%t}`, colorize))
|
||||
} else {
|
||||
log.NewLogger(1000, "doctor", "file", fmt.Sprintf(`{"filename":%q,"level":"trace","stacktracelevel":"NONE"}`, logFile))
|
||||
}
|
||||
|
||||
// Finally redirect the default golog to here
|
||||
golog.SetFlags(0)
|
||||
golog.SetPrefix("")
|
||||
|
|
|
@ -123,7 +123,7 @@ func initEmbeddedExtractor(c *cli.Context) error {
|
|||
|
||||
sections["public"] = §ion{Path: "public", Names: public.AssetNames, IsDir: public.AssetIsDir, Asset: public.Asset}
|
||||
sections["options"] = §ion{Path: "options", Names: options.AssetNames, IsDir: options.AssetIsDir, Asset: options.Asset}
|
||||
sections["templates"] = §ion{Path: "templates", Names: templates.AssetNames, IsDir: templates.AssetIsDir, Asset: templates.Asset}
|
||||
sections["templates"] = §ion{Path: "templates", Names: templates.BuiltinAssetNames, IsDir: templates.BuiltinAssetIsDir, Asset: templates.BuiltinAsset}
|
||||
|
||||
for _, sec := range sections {
|
||||
assets = append(assets, buildAssetList(sec, pats, c)...)
|
||||
|
|
|
@ -112,11 +112,8 @@ func migrateRepoAvatars(ctx context.Context, dstStorage storage.ObjectStorage) e
|
|||
|
||||
func migrateRepoArchivers(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||
return db.IterateObjects(ctx, func(archiver *repo_model.RepoArchiver) error {
|
||||
p, err := archiver.RelativePath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = storage.Copy(dstStorage, p, storage.RepoArchives, p)
|
||||
p := archiver.RelativePath()
|
||||
_, err := storage.Copy(dstStorage, p, storage.RepoArchives, p)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
|
|
@ -53,8 +53,7 @@ func TestMigratePackages(t *testing.T) {
|
|||
|
||||
ctx := context.Background()
|
||||
|
||||
p, err := os.MkdirTemp(os.TempDir(), "migrated_packages")
|
||||
assert.NoError(t, err)
|
||||
p := t.TempDir()
|
||||
|
||||
dstStorage, err := storage.NewLocalStorage(
|
||||
ctx,
|
||||
|
|
34
cmd/web.go
34
cmd/web.go
|
@ -76,7 +76,7 @@ func runHTTPRedirector() {
|
|||
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
||||
})
|
||||
|
||||
err := runHTTP("tcp", source, "HTTP Redirector", handler)
|
||||
err := runHTTP("tcp", source, "HTTP Redirector", handler, setting.RedirectorUseProxyProtocol)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to start port redirection: %v", err)
|
||||
}
|
||||
|
@ -126,8 +126,10 @@ func runWeb(ctx *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
c := install.Routes()
|
||||
installCtx, cancel := context.WithCancel(graceful.GetManager().HammerContext())
|
||||
c := install.Routes(installCtx)
|
||||
err := listen(c, false)
|
||||
cancel()
|
||||
if err != nil {
|
||||
log.Critical("Unable to open listener for installer. Is Gitea already running?")
|
||||
graceful.GetManager().DoGracefulShutdown()
|
||||
|
@ -175,7 +177,7 @@ func runWeb(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
// Set up Chi routes
|
||||
c := routers.NormalRoutes()
|
||||
c := routers.NormalRoutes(graceful.GetManager().HammerContext())
|
||||
err := listen(c, true)
|
||||
<-graceful.GetManager().Done()
|
||||
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
||||
|
@ -231,40 +233,38 @@ func listen(m http.Handler, handleRedirector bool) error {
|
|||
if handleRedirector {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runHTTP("tcp", listenAddr, "Web", m)
|
||||
err = runHTTP("tcp", listenAddr, "Web", m, setting.UseProxyProtocol)
|
||||
case setting.HTTPS:
|
||||
if setting.EnableAcme {
|
||||
err = runACME(listenAddr, m)
|
||||
break
|
||||
} else {
|
||||
if handleRedirector {
|
||||
if setting.RedirectOtherPort {
|
||||
go runHTTPRedirector()
|
||||
} else {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
}
|
||||
err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, m)
|
||||
}
|
||||
if handleRedirector {
|
||||
if setting.RedirectOtherPort {
|
||||
go runHTTPRedirector()
|
||||
} else {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
}
|
||||
err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, m, setting.UseProxyProtocol, setting.ProxyProtocolTLSBridging)
|
||||
case setting.FCGI:
|
||||
if handleRedirector {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runFCGI("tcp", listenAddr, "FCGI Web", m)
|
||||
err = runFCGI("tcp", listenAddr, "FCGI Web", m, setting.UseProxyProtocol)
|
||||
case setting.HTTPUnix:
|
||||
if handleRedirector {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runHTTP("unix", listenAddr, "Web", m)
|
||||
err = runHTTP("unix", listenAddr, "Web", m, setting.UseProxyProtocol)
|
||||
case setting.FCGIUnix:
|
||||
if handleRedirector {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runFCGI("unix", listenAddr, "Web", m)
|
||||
err = runFCGI("unix", listenAddr, "Web", m, setting.UseProxyProtocol)
|
||||
default:
|
||||
log.Fatal("Invalid protocol: %s", setting.Protocol)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Critical("Failed to start server: %v", err)
|
||||
}
|
||||
|
|
|
@ -113,14 +113,14 @@ func runACME(listenAddr string, m http.Handler) error {
|
|||
|
||||
log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
|
||||
// all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
|
||||
err := runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)))
|
||||
err := runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)), setting.RedirectorUseProxyProtocol)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return runHTTPSWithTLSConfig("tcp", listenAddr, "Web", tlsConfig, m)
|
||||
return runHTTPSWithTLSConfig("tcp", listenAddr, "Web", tlsConfig, m, setting.UseProxyProtocol, setting.ProxyProtocolTLSBridging)
|
||||
}
|
||||
|
||||
func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -15,8 +15,8 @@ import (
|
|||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
func runHTTP(network, listenAddr, name string, m http.Handler) error {
|
||||
return graceful.HTTPListenAndServe(network, listenAddr, name, m)
|
||||
func runHTTP(network, listenAddr, name string, m http.Handler, useProxyProtocol bool) error {
|
||||
return graceful.HTTPListenAndServe(network, listenAddr, name, m, useProxyProtocol)
|
||||
}
|
||||
|
||||
// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
|
||||
|
@ -36,7 +36,7 @@ func NoInstallListener() {
|
|||
graceful.GetManager().InformCleanup()
|
||||
}
|
||||
|
||||
func runFCGI(network, listenAddr, name string, m http.Handler) error {
|
||||
func runFCGI(network, listenAddr, name string, m http.Handler, useProxyProtocol bool) error {
|
||||
// This needs to handle stdin as fcgi point
|
||||
fcgiServer := graceful.NewServer(network, listenAddr, name)
|
||||
|
||||
|
@ -47,7 +47,7 @@ func runFCGI(network, listenAddr, name string, m http.Handler) error {
|
|||
}
|
||||
m.ServeHTTP(resp, req)
|
||||
}))
|
||||
})
|
||||
}, useProxyProtocol)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to start FCGI main server: %v", err)
|
||||
}
|
||||
|
|
|
@ -129,14 +129,14 @@ var (
|
|||
defaultCiphersChaChaFirst = append(defaultCiphersChaCha, defaultCiphersAES...)
|
||||
)
|
||||
|
||||
// runHTTPs listens on the provided network address and then calls
|
||||
// runHTTPS listens on the provided network address and then calls
|
||||
// Serve to handle requests on incoming TLS connections.
|
||||
//
|
||||
// Filenames containing a certificate and matching private key for the server must
|
||||
// be provided. If the certificate is signed by a certificate authority, the
|
||||
// certFile should be the concatenation of the server's certificate followed by the
|
||||
// CA's certificate.
|
||||
func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler) error {
|
||||
func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler, useProxyProtocol, proxyProtocolTLSBridging bool) error {
|
||||
tlsConfig := &tls.Config{}
|
||||
if tlsConfig.NextProtos == nil {
|
||||
tlsConfig.NextProtos = []string{"h2", "http/1.1"}
|
||||
|
@ -184,9 +184,9 @@ func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handle
|
|||
return err
|
||||
}
|
||||
|
||||
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m)
|
||||
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m, useProxyProtocol, proxyProtocolTLSBridging)
|
||||
}
|
||||
|
||||
func runHTTPSWithTLSConfig(network, listenAddr, name string, tlsConfig *tls.Config, m http.Handler) error {
|
||||
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m)
|
||||
func runHTTPSWithTLSConfig(network, listenAddr, name string, tlsConfig *tls.Config, m http.Handler, useProxyProtocol, proxyProtocolTLSBridging bool) error {
|
||||
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m, useProxyProtocol, proxyProtocolTLSBridging)
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
gitea_git "code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/external"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
|
@ -111,13 +112,13 @@ func runPR() {
|
|||
unittest.LoadFixtures()
|
||||
util.RemoveAll(setting.RepoRootPath)
|
||||
util.RemoveAll(repo_module.LocalCopyPath())
|
||||
unittest.CopyDir(path.Join(curDir, "integrations/gitea-repositories-meta"), setting.RepoRootPath)
|
||||
unittest.CopyDir(path.Join(curDir, "tests/gitea-repositories-meta"), setting.RepoRootPath)
|
||||
|
||||
log.Printf("[PR] Setting up router\n")
|
||||
// routers.GlobalInit()
|
||||
external.RegisterRenderers()
|
||||
markup.Init()
|
||||
c := routers.NormalRoutes()
|
||||
c := routers.NormalRoutes(graceful.GetManager().HammerContext())
|
||||
|
||||
log.Printf("[PR] Ready for testing !\n")
|
||||
log.Printf("[PR] Login with user1, user2, user3, ... with pass: password\n")
|
||||
|
|
|
@ -29,6 +29,18 @@ RUN_MODE = ; prod
|
|||
;; The protocol the server listens on. One of 'http', 'https', 'unix' or 'fcgi'. Defaults to 'http'
|
||||
;PROTOCOL = http
|
||||
;;
|
||||
;; Expect PROXY protocol headers on connections
|
||||
;USE_PROXY_PROTOCOL = false
|
||||
;;
|
||||
;; Use PROXY protocol in TLS Bridging mode
|
||||
;PROXY_PROTOCOL_TLS_BRIDGING = false
|
||||
;;
|
||||
; Timeout to wait for PROXY protocol header (set to 0 to have no timeout)
|
||||
;PROXY_PROTOCOL_HEADER_TIMEOUT=5s
|
||||
;;
|
||||
; Accept PROXY protocol headers with UNKNOWN type
|
||||
;PROXY_PROTOCOL_ACCEPT_UNKNOWN=false
|
||||
;;
|
||||
;; Set the domain for the server
|
||||
;DOMAIN = localhost
|
||||
;;
|
||||
|
@ -51,6 +63,8 @@ RUN_MODE = ; prod
|
|||
;REDIRECT_OTHER_PORT = false
|
||||
;PORT_TO_REDIRECT = 80
|
||||
;;
|
||||
;; expect PROXY protocol header on connections to https redirector.
|
||||
;REDIRECTOR_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL)
|
||||
;; Minimum and maximum supported TLS versions
|
||||
;SSL_MIN_VERSION=TLSv1.2
|
||||
;SSL_MAX_VERSION=
|
||||
|
@ -76,13 +90,19 @@ RUN_MODE = ; prod
|
|||
;; Do not set this variable if PROTOCOL is set to 'unix'.
|
||||
;LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
|
||||
;;
|
||||
;; When making local connections pass the PROXY protocol header.
|
||||
;LOCAL_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL)
|
||||
;;
|
||||
;; Disable SSH feature when not available
|
||||
;DISABLE_SSH = false
|
||||
;;
|
||||
;; Whether to use the builtin SSH server or not.
|
||||
;START_SSH_SERVER = false
|
||||
;;
|
||||
;; Username to use for the builtin SSH server.
|
||||
;; Expect PROXY protocol header on connections to the built-in SSH server
|
||||
;SSH_SERVER_USE_PROXY_PROTOCOL = false
|
||||
;;
|
||||
;; Username to use for the builtin SSH server. If blank, then it is the value of RUN_USER.
|
||||
;BUILTIN_SSH_SERVER_USER = %(RUN_USER)s
|
||||
;;
|
||||
;; Domain name to be exposed in clone URL
|
||||
|
@ -1144,6 +1164,10 @@ ROUTER = console
|
|||
;;
|
||||
;; Whether to enable a Service Worker to cache frontend assets
|
||||
;USE_SERVICE_WORKER = false
|
||||
;;
|
||||
;; Whether to only show relevant repos on the explore page when no keyword is specified and default sorting is used.
|
||||
;; A repo is considered irrelevant if it's a fork or if it has no metadata (no description, no icon, no topic).
|
||||
;ONLY_SHOW_RELEVANT_REPOS = false
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -5,7 +5,7 @@ mkdir -p ${HOME} && chmod 0700 ${HOME}
|
|||
if [ ! -w ${HOME} ]; then echo "${HOME} is not writable"; exit 1; fi
|
||||
|
||||
# Prepare custom folder
|
||||
mkdir -p ${GITEA_CUSTOM} && chmod 0500 ${GITEA_CUSTOM}
|
||||
mkdir -p ${GITEA_CUSTOM} && chmod 0700 ${GITEA_CUSTOM}
|
||||
|
||||
# Prepare temp folder
|
||||
mkdir -p ${GITEA_TEMP} && chmod 0700 ${GITEA_TEMP}
|
||||
|
|
3
docs/.gitignore
vendored
3
docs/.gitignore
vendored
|
@ -2,3 +2,6 @@ public/
|
|||
templates/swagger/v1_json.tmpl
|
||||
themes/
|
||||
resources/
|
||||
|
||||
# Temporary lock file while building
|
||||
/.hugo_build.lock
|
||||
|
|
|
@ -18,7 +18,7 @@ params:
|
|||
description: Git with a cup of tea
|
||||
author: The Gitea Authors
|
||||
website: https://docs.gitea.io
|
||||
version: 1.16.9
|
||||
version: 1.17.1
|
||||
minGoVersion: 1.18
|
||||
goVersion: 1.19
|
||||
minNodeVersion: 14
|
||||
|
|
|
@ -194,6 +194,8 @@ The following configuration set `Content-Type: application/vnd.android.package-a
|
|||
- `DEFAULT_SHOW_FULL_NAME`: **false**: Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used.
|
||||
- `SEARCH_REPO_DESCRIPTION`: **true**: Whether to search within description at repository search on explore page.
|
||||
- `USE_SERVICE_WORKER`: **false**: Whether to enable a Service Worker to cache frontend assets.
|
||||
- `ONLY_SHOW_RELEVANT_REPOS`: **false** Whether to only show relevant repos on the explore page when no keyword is specified and default sorting is used.
|
||||
A repo is considered irrelevant if it's a fork or if it has no metadata (no description, no icon, no topic).
|
||||
|
||||
### UI - Admin (`ui.admin`)
|
||||
|
||||
|
@ -238,6 +240,10 @@ The following configuration set `Content-Type: application/vnd.android.package-a
|
|||
## Server (`server`)
|
||||
|
||||
- `PROTOCOL`: **http**: \[http, https, fcgi, http+unix, fcgi+unix\]
|
||||
- `USE_PROXY_PROTOCOL`: **false**: Expect PROXY protocol headers on connections
|
||||
- `PROXY_PROTOCOL_TLS_BRIDGING`: **false**: When protocol is https, expect PROXY protocol headers after TLS negotiation.
|
||||
- `PROXY_PROTOCOL_HEADER_TIMEOUT`: **5s**: Timeout to wait for PROXY protocol header (set to 0 to have no timeout)
|
||||
- `PROXY_PROTOCOL_ACCEPT_UNKNOWN`: **false**: Accept PROXY protocol headers with Unknown type.
|
||||
- `DOMAIN`: **localhost**: Domain name of this server.
|
||||
- `ROOT_URL`: **%(PROTOCOL)s://%(DOMAIN)s:%(HTTP\_PORT)s/**:
|
||||
Overwrite the automatically generated public URL.
|
||||
|
@ -262,12 +268,15 @@ The following configuration set `Content-Type: application/vnd.android.package-a
|
|||
most cases you do not need to change the default value. Alter it only if
|
||||
your SSH server node is not the same as HTTP node. Do not set this variable
|
||||
if `PROTOCOL` is set to `http+unix`.
|
||||
- `LOCAL_USE_PROXY_PROTOCOL`: **%(USE_PROXY_PROTOCOL)**: When making local connections pass the PROXY protocol header.
|
||||
This should be set to false if the local connection will go through the proxy.
|
||||
- `PER_WRITE_TIMEOUT`: **30s**: Timeout for any write to the connection. (Set to -1 to
|
||||
disable all timeouts.)
|
||||
- `PER_WRITE_PER_KB_TIMEOUT`: **10s**: Timeout per Kb written to connections.
|
||||
|
||||
- `DISABLE_SSH`: **false**: Disable SSH feature when it's not available.
|
||||
- `START_SSH_SERVER`: **false**: When enabled, use the built-in SSH server.
|
||||
- `SSH_SERVER_USE_PROXY_PROTOCOL`: **false**: Expect PROXY protocol header on connections to the built-in SSH Server.
|
||||
- `BUILTIN_SSH_SERVER_USER`: **%(RUN_USER)s**: Username to use for the built-in SSH Server.
|
||||
- `SSH_USER`: **%(BUILTIN_SSH_SERVER_USER)**: SSH username displayed in clone URLs. This is only for people who configure the SSH server themselves; in most cases, you want to leave this blank and modify the `BUILTIN_SSH_SERVER_USER`.
|
||||
- `SSH_DOMAIN`: **%(DOMAIN)s**: Domain name of this server, used for displayed clone URL.
|
||||
|
@ -313,6 +322,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
|
|||
- `LFS_LOCKS_PAGING_NUM`: **50**: Maximum number of LFS Locks returned per page.
|
||||
|
||||
- `REDIRECT_OTHER_PORT`: **false**: If true and `PROTOCOL` is https, allows redirecting http requests on `PORT_TO_REDIRECT` to the https port Gitea listens on.
|
||||
- `REDIRECTOR_USE_PROXY_PROTOCOL`: **%(USE_PROXY_PROTOCOL)**: expect PROXY protocol header on connections to https redirector.
|
||||
- `PORT_TO_REDIRECT`: **80**: Port for the http redirection service to listen on. Used when `REDIRECT_OTHER_PORT` is true.
|
||||
- `SSL_MIN_VERSION`: **TLSv1.2**: Set the minimum version of ssl support.
|
||||
- `SSL_MAX_VERSION`: **\<empty\>**: Set the maximum version of ssl support.
|
||||
|
|
|
@ -49,7 +49,7 @@ Note that `/users/:name/tokens` is a special endpoint and requires you
|
|||
to authenticate using `BasicAuth` and a password, as follows:
|
||||
|
||||
```sh
|
||||
$ curl -XPOST -H "Content-Type: application/json" -k -d '{"name":"test"}' -u username:password https://gitea.your.host/api/v1/users/<username>/tokens
|
||||
$ curl -H "Content-Type: application/json" -d '{"name":"test"}' -u username:password https://gitea.your.host/api/v1/users/<username>/tokens
|
||||
{"id":1,"name":"test","sha1":"9fcb1158165773dd010fca5f0cf7174316c3e37d","token_last_eight":"16c3e37d"}
|
||||
```
|
||||
|
||||
|
@ -58,7 +58,7 @@ plain-text. It will not be displayed when listing tokens with a `GET`
|
|||
request; e.g.
|
||||
|
||||
```sh
|
||||
$ curl --request GET --url https://yourusername:password@gitea.your.host/api/v1/users/<username>/tokens
|
||||
$ curl --url https://yourusername:password@gitea.your.host/api/v1/users/<username>/tokens
|
||||
[{"name":"test","sha1":"","token_last_eight:"........":},{"name":"dev","sha1":"","token_last_eight":"........"}]
|
||||
```
|
||||
|
||||
|
@ -70,7 +70,7 @@ is where you'd place the code from your authenticator.
|
|||
Here is how the request would look like in curl:
|
||||
|
||||
```sh
|
||||
$ curl -H "X-Gitea-OTP: 123456" --request GET --url https://yourusername:yourpassword@gitea.your.host/api/v1/users/yourusername/tokens
|
||||
$ curl -H "X-Gitea-OTP: 123456" --url https://yourusername:yourpassword@gitea.your.host/api/v1/users/yourusername/tokens
|
||||
```
|
||||
|
||||
You can also create an API key token via your Gitea installation's web
|
||||
|
@ -96,7 +96,7 @@ Authorization: token 65eaa9c8ef52460d22a93307fe0aee76289dc675
|
|||
In a `curl` command, for instance, this would look like:
|
||||
|
||||
```sh
|
||||
curl -X POST "http://localhost:4000/api/v1/repos/test1/test1/issues" \
|
||||
curl "http://localhost:4000/api/v1/repos/test1/test1/issues" \
|
||||
-H "accept: application/json" \
|
||||
-H "Authorization: token 65eaa9c8ef52460d22a93307fe0aee76289dc675" \
|
||||
-H "Content-Type: application/json" -d "{ \"body\": \"testing\", \"title\": \"test 20\"}" -i
|
||||
|
|
|
@ -46,7 +46,7 @@ Authorization: token 65eaa9c8ef52460d22a93307fe0aee76289dc675
|
|||
以 `curl` 命令为例,它会以如下形式携带在请求中:
|
||||
|
||||
```
|
||||
curl -X POST "http://localhost:4000/api/v1/repos/test1/test1/issues" \
|
||||
curl "http://localhost:4000/api/v1/repos/test1/test1/issues" \
|
||||
-H "accept: application/json" \
|
||||
-H "Authorization: token 65eaa9c8ef52460d22a93307fe0aee76289dc675" \
|
||||
-H "Content-Type: application/json" -d "{ \"body\": \"testing\", \"title\": \"test 20\"}" -i
|
||||
|
@ -62,7 +62,7 @@ curl -X POST "http://localhost:4000/api/v1/repos/test1/test1/issues" \
|
|||
### 使用 Basic authentication 认证:
|
||||
|
||||
```
|
||||
$ curl --request GET --url https://yourusername:yourpassword@gitea.your.host/api/v1/users/yourusername/tokens
|
||||
$ curl --url https://yourusername:yourpassword@gitea.your.host/api/v1/users/yourusername/tokens
|
||||
[{"name":"test","sha1":"..."},{"name":"dev","sha1":"..."}]
|
||||
```
|
||||
|
||||
|
|
|
@ -33,7 +33,9 @@ To maintain understandable code and avoid circular dependencies it is important
|
|||
|
||||
- `build`: Scripts to help build Gitea.
|
||||
- `cmd`: All Gitea actual sub commands includes web, doctor, serv, hooks, admin and etc. `web` will start the web service. `serv` and `hooks` will be invoked by Git or OpenSSH. Other sub commands could help to maintain Gitea.
|
||||
- `integrations`: Integration tests
|
||||
- `tests`: Common test utility functions
|
||||
- `tests/integration`: Integration tests, to test back-end regressions
|
||||
- `tests/e2e`: E2e tests, to test test front-end <> back-end compatibility and visual regressions.
|
||||
- `models`: Contains the data structures used by xorm to construct database tables. It also contains functions to query and update the database. Dependencies to other Gitea code should be avoided. You can make exceptions in cases such as logging.
|
||||
- `models/db`: Basic database operations. All other `models/xxx` packages should depend on this package. The `GetEngine` function should only be invoked from `models/`.
|
||||
- `models/fixtures`: Sample data used in unit tests and integration tests. One `yml` file means one table which will be loaded into database when beginning the tests.
|
||||
|
|
|
@ -309,7 +309,7 @@ will run the integration tests in an SQLite environment. Integration tests
|
|||
require `git lfs` to be installed. Other database tests are available but
|
||||
may need adjustment to the local environment.
|
||||
|
||||
Take a look at [`integrations/README.md`](https://github.com/go-gitea/gitea/blob/main/integrations/README.md)
|
||||
Take a look at [`tests/integration/README.md`](https://github.com/go-gitea/gitea/blob/main/tests/integration/README.md)
|
||||
for more information and how to run a single test.
|
||||
|
||||
### Testing for a PR
|
||||
|
|
|
@ -35,24 +35,24 @@ _Symbols used in table:_
|
|||
|
||||
| Feature | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
|
||||
| ----------------------------------- | ---------------------------------------------------| ---- | --------- | --------- | --------- | -------------- | ------------ |
|
||||
| Open source and free | ✓ | ✓ | ✘ | ✓ | ✘ | ✘ | ✓ |
|
||||
| Low resource usage (RAM/CPU) | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✘ |
|
||||
| Multiple database support | ✓ | ✓ | ✘ | ⁄ | ⁄ | ✓ | ✓ |
|
||||
| Multiple OS support | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✓ |
|
||||
| Easy upgrade process | ✓ | ✓ | ✘ | ✓ | ✓ | ✘ | ✓ |
|
||||
| Markdown support | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Orgmode support | ✓ | ✘ | ✓ | ✘ | ✘ | ✘ | ? |
|
||||
| CSV support | ✓ | ✘ | ✓ | ✘ | ✘ | ✓ | ? |
|
||||
| Third-party render tool support | ✓ | ✘ | ✘ | ✘ | ✘ | ✓ | ? |
|
||||
| Static Git-powered pages | [✘](https://github.com/go-gitea/gitea/issues/302) | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Integrated Git-powered wiki | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ (cloud only) | ✘ |
|
||||
| Deploy Tokens | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Repository Tokens with write rights | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Built-in Package/Container Registry | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| External git mirroring | ✓ | ✓ | ✘ | ✘ | ✓ | ✓ | ✓ |
|
||||
| WebAuthn (2FA) | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ? |
|
||||
| Built-in CI/CD | ✘ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Subgroups: groups within groups | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✓ |
|
||||
| Open source and free | ✓ | ✓ | ✘ | ✓ | ✘ | ✘ | ✓ |
|
||||
| Low resource usage (RAM/CPU) | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✘ |
|
||||
| Multiple database support | ✓ | ✓ | ✘ | ⁄ | ⁄ | ✓ | ✓ |
|
||||
| Multiple OS support | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✓ |
|
||||
| Easy upgrade process | ✓ | ✓ | ✘ | ✓ | ✓ | ✘ | ✓ |
|
||||
| Markdown support | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Orgmode support | ✓ | ✘ | ✓ | ✘ | ✘ | ✘ | ? |
|
||||
| CSV support | ✓ | ✘ | ✓ | ✘ | ✘ | ✓ | ? |
|
||||
| Third-party render tool support | ✓ | ✘ | ✘ | ✘ | ✘ | ✓ | ? |
|
||||
| Static Git-powered pages | [✘](https://github.com/go-gitea/gitea/issues/302) | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Integrated Git-powered wiki | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ (cloud only) | ✘ |
|
||||
| Deploy Tokens | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Repository Tokens with write rights | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Built-in Package/Container Registry | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| External git mirroring | ✓ | ✓ | ✘ | ✘ | ✓ | ✓ | ✓ |
|
||||
| WebAuthn (2FA) | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ? |
|
||||
| Built-in CI/CD | ✘ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Subgroups: groups within groups | [✘](https://github.com/go-gitea/gitea/issues/1872) | ✘ | ✘ | ✓ | ✓ | ✘ | ✓ |
|
||||
|
||||
## Code management
|
||||
|
||||
|
|
82
docs/content/doc/installation/on-kubernetes.zh-cn.md
Normal file
82
docs/content/doc/installation/on-kubernetes.zh-cn.md
Normal file
|
@ -0,0 +1,82 @@
|
|||
---
|
||||
date: "2020-03-19T19:27:00+02:00"
|
||||
title: "在 Kubernetes 安装 Gitea"
|
||||
slug: "install-on-kubernetes"
|
||||
weight: 10
|
||||
toc: false
|
||||
draft: false
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "installation"
|
||||
name: "Kubernetes"
|
||||
weight: 50
|
||||
identifier: "install-on-kubernetes"
|
||||
---
|
||||
|
||||
# 使用 Helm 在 Kubernetes 云原生环境中安装 Gitea
|
||||
|
||||
Gitea 已经提供了便于在 Kubernetes 云原生环境中安装所需的 Helm Chart
|
||||
|
||||
默认安装指令为:
|
||||
|
||||
```bash
|
||||
helm repo add gitea https://dl.gitea.io/charts
|
||||
helm repo update
|
||||
helm install gitea gitea/gitea
|
||||
```
|
||||
|
||||
如果采用默认安装指令,Helm 会部署单实例的 Gitea, PostgreSQL, Memcached。若您想实现自定义安装(包括配置 Gitea 集群、NGINX Ingress、MySQL、MariaDB、持久存储等),请前往阅读:[Gitea Helm Chart](https://gitea.com/gitea/helm-chart/)
|
||||
|
||||
您也可以通过 `helm show` 命令导出 `README.md` 和配置文件 `values.yaml` 进行学习和编辑,例如:
|
||||
|
||||
```bash
|
||||
helm show values gitea/gitea > values.yaml
|
||||
helm show readme gitea/gitea > README.md
|
||||
|
||||
# 使用自定义的配置文件 values.yaml
|
||||
helm install gitea -f values.yaml gitea/gitea
|
||||
```
|
||||
|
||||
## 运行状况检查接口
|
||||
|
||||
Gitea 附带了一个运行状况检查接口 `/api/healthz`,你可以像这样在 Kubernetes 中配置它:
|
||||
|
||||
```yaml
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /api/healthz
|
||||
port: http
|
||||
initialDelaySeconds: 200
|
||||
timeoutSeconds: 5
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
failureThreshold: 10
|
||||
```
|
||||
|
||||
成功的运行状况检查响应代码为 HTTP `200`,下面是示例:
|
||||
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
|
||||
|
||||
{
|
||||
"status": "pass",
|
||||
"description": "Gitea: Git with a cup of tea",
|
||||
"checks": {
|
||||
"cache:ping": [
|
||||
{
|
||||
"status": "pass",
|
||||
"time": "2022-02-19T09:16:08Z"
|
||||
}
|
||||
],
|
||||
"database:ping": [
|
||||
{
|
||||
"status": "pass",
|
||||
"time": "2022-02-19T09:16:08Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
有关更多信息,请参考 Kubernetes 文档 [配置存活、就绪和启动探测器](https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)
|
|
@ -49,6 +49,16 @@ Open "Windows Services", search for the service named "gitea", right-click it an
|
|||
"Run". If everything is OK, Gitea will be reachable on `http://localhost:3000` (or the port
|
||||
that was configured).
|
||||
|
||||
## Adding startup dependancies
|
||||
|
||||
To add a startup dependancy to the Gitea Windows service (eg Mysql, Mariadb), as an Administrator, then run the following command:
|
||||
|
||||
```
|
||||
sc.exe config gitea depend= mariadb
|
||||
```
|
||||
|
||||
This will ensure that when the Windows machine restarts, the automatic starting of Gitea is postponed until the database is ready and thus mitigate failed startups.
|
||||
|
||||
## Unregister as a service
|
||||
|
||||
To unregister Gitea as a service, open a command prompt (cmd) as an Administrator and run:
|
||||
|
|
|
@ -37,6 +37,7 @@ The following package managers are currently supported:
|
|||
| [Pub]({{< relref "doc/packages/pub.en-us.md" >}}) | Dart | `dart`, `flutter` |
|
||||
| [PyPI]({{< relref "doc/packages/pypi.en-us.md" >}}) | Python | `pip`, `twine` |
|
||||
| [RubyGems]({{< relref "doc/packages/rubygems.en-us.md" >}}) | Ruby | `gem`, `Bundler` |
|
||||
| [Vagrant]({{< relref "doc/packages/vagrant.en-us.md" >}}) | - | `vagrant` |
|
||||
|
||||
**The following paragraphs only apply if Packages are not globally disabled!**
|
||||
|
||||
|
|
78
docs/content/doc/packages/vagrant.en-us.md
Normal file
78
docs/content/doc/packages/vagrant.en-us.md
Normal file
|
@ -0,0 +1,78 @@
|
|||
---
|
||||
date: "2022-08-23T00:00:00+00:00"
|
||||
title: "Vagrant Packages Repository"
|
||||
slug: "packages/vagrant"
|
||||
draft: false
|
||||
toc: false
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "packages"
|
||||
name: "vagrant"
|
||||
weight: 120
|
||||
identifier: "vagrant"
|
||||
---
|
||||
|
||||
# Vagrant Packages Repository
|
||||
|
||||
Publish [Vagrant](https://www.vagrantup.com/) packages for your user or organization.
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
{{< toc >}}
|
||||
|
||||
## Requirements
|
||||
|
||||
To work with the Vagrant package registry, you need [Vagrant](https://www.vagrantup.com/downloads) and a tool to make HTTP requests like `curl`.
|
||||
|
||||
## Publish a package
|
||||
|
||||
Publish a Vagrant box by performing a HTTP PUT request to the registry:
|
||||
|
||||
```
|
||||
PUT https://gitea.example.com/api/packages/{owner}/vagrant/{package_name}/{package_version}/{provider}.box
|
||||
```
|
||||
|
||||
| Parameter | Description |
|
||||
| ----------------- | ----------- |
|
||||
| `owner` | The owner of the package. |
|
||||
| `package_name` | The package name. |
|
||||
| `package_version` | The package version, semver compatible. |
|
||||
| `provider` | One of the [supported provider names](https://www.vagrantup.com/docs/providers). |
|
||||
|
||||
Example for uploading a Hyper-V box:
|
||||
|
||||
```shell
|
||||
curl --user your_username:your_password_or_token \
|
||||
--upload-file path/to/your/vagrant.box \
|
||||
https://gitea.example.com/api/packages/testuser/vagrant/test_system/1.0.0/hyperv.box
|
||||
```
|
||||
|
||||
You cannot publish a box if a box of the same name, version and provider already exists. You must delete the existing package first.
|
||||
|
||||
## Install a package
|
||||
|
||||
To install a box from the package registry, execute the following command:
|
||||
|
||||
```shell
|
||||
vagrant box add "https://gitea.example.com/api/packages/{owner}/vagrant/{package_name}"
|
||||
```
|
||||
|
||||
| Parameter | Description |
|
||||
| -------------- | ----------- |
|
||||
| `owner` | The owner of the package. |
|
||||
| `package_name` | The package name. |
|
||||
|
||||
For example:
|
||||
|
||||
```shell
|
||||
vagrant box add "https://gitea.example.com/api/packages/testuser/vagrant/test_system"
|
||||
```
|
||||
|
||||
This will install the latest version of the package. To add a specific version, use the `--box-version` parameter.
|
||||
If the registry is private you can pass your [personal access token]({{< relref "doc/developers/api-usage.en-us.md#authentication" >}}) in the `VAGRANT_CLOUD_TOKEN` environment variable.
|
||||
|
||||
## Supported commands
|
||||
|
||||
```
|
||||
vagrant box add
|
||||
```
|
47
docs/content/doc/usage/agit-support.en-us.md
Normal file
47
docs/content/doc/usage/agit-support.en-us.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
date: " 2022-09-01T20:50:42+0000"
|
||||
title: "Usage: Agit Setup"
|
||||
slug: "agit-setup"
|
||||
weight: 12
|
||||
toc: false
|
||||
draft: false
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "usage"
|
||||
name: "Agit Setup"
|
||||
weight: 12
|
||||
identifier: "agit-setup"
|
||||
---
|
||||
|
||||
# Agit Setup
|
||||
|
||||
In Gitea `1.13`, support for [agit](https://git-repo.info/en/2020/03/agit-flow-and-git-repo/) was added.
|
||||
|
||||
## Creating PRs with Agit
|
||||
|
||||
Agit allows to create PRs while pushing code to the remote repo. \
|
||||
This can be done by pushing to the branch followed by a specific refspec (a location identifier known to git). \
|
||||
The following example illustrates this:
|
||||
|
||||
```shell
|
||||
git push origin HEAD:refs/for/master
|
||||
```
|
||||
|
||||
The command has the following structure:
|
||||
|
||||
- `HEAD`: The target branch
|
||||
- `refs/<for|draft|for-review>/<branch>`: The target PR type
|
||||
- `for`: Create a normal PR with `<branch>` as the target branch
|
||||
- `draft`/ `for-review`: Currently ignored silently
|
||||
- `<branch>/<session>`: The target branch to open the PR
|
||||
- `-o <topic|title|description>`: Options for the PR
|
||||
- `title`: The PR title
|
||||
- `topic`: The branch name the PR should be opened for
|
||||
- `description`: The PR description
|
||||
- `force-push`: confirm force update the target branch
|
||||
|
||||
Here's another advanced example for creating a new PR targeting `master` with `topic`, `title`, and `description`:
|
||||
|
||||
```shell
|
||||
git push origin HEAD:refs/for/master -o topic="Topic of my PR" -o title="Title of the PR" -o description="# The PR Description\nThis can be **any** markdown content.\n- [x] Ok"
|
||||
```
|
|
@ -62,7 +62,7 @@ The SQL dump created by `gitea dump` uses XORM and Gitea admins may prefer to us
|
|||
# mysql
|
||||
mysqldump -u$USER -p$PASS --database $DATABASE > gitea-db.sql
|
||||
# postgres
|
||||
pgdump -U $USER $DATABASE > gitea-db.sql
|
||||
pg_dump -U $USER $DATABASE > gitea-db.sql
|
||||
```
|
||||
|
||||
### Using Docker (`dump`)
|
||||
|
|
|
@ -72,7 +72,7 @@ ACME_DIRECTORY=https
|
|||
ACME_EMAIL=email@example.com
|
||||
```
|
||||
|
||||
Minimumg setup using a [smallstep CA](https://github.com/smallstep/certificates), refer to [their tutorial](https://smallstep.com/docs/tutorials/acme-challenge) for more information.
|
||||
Minimum setup using a [smallstep CA](https://github.com/smallstep/certificates), refer to [their tutorial](https://smallstep.com/docs/tutorials/acme-challenge) for more information.
|
||||
|
||||
```ini
|
||||
[server]
|
||||
|
|
|
@ -25,51 +25,53 @@ main branch of the repository so that they can autopopulate the form when users
|
|||
creating issues and pull requests. This will cut down on the initial back and forth
|
||||
of getting some clarifying details.
|
||||
|
||||
Additionally, the New Issue page URL can be suffixed with `?title=Issue+Title&body=Issue+Text` and the form will be populated with those strings. Those strings will be used instead of the template if there is one.
|
||||
|
||||
## File names
|
||||
|
||||
Possible file names for issue templates:
|
||||
|
||||
- `ISSUE_TEMPLATE.md`
|
||||
- `ISSUE_TEMPLATE.yaml`
|
||||
- `ISSUE_TEMPLATE.yml`
|
||||
- `issue_template.md`
|
||||
- `issue_template.yaml`
|
||||
- `issue_template.yml`
|
||||
- `.gitea/ISSUE_TEMPLATE.md`
|
||||
- `.gitea/ISSUE_TEMPLATE.yaml`
|
||||
- `.gitea/ISSUE_TEMPLATE.yml`
|
||||
- `.gitea/issue_template.md`
|
||||
- `.gitea/issue_template.yaml`
|
||||
- `.gitea/issue_template.md`
|
||||
- `.github/ISSUE_TEMPLATE.md`
|
||||
- `.github/ISSUE_TEMPLATE.yaml`
|
||||
- `.github/ISSUE_TEMPLATE.yml`
|
||||
- `.github/issue_template.md`
|
||||
- `.github/issue_template.yaml`
|
||||
- `.github/issue_template.yml`
|
||||
|
||||
Possible file names for PR templates:
|
||||
|
||||
- `PULL_REQUEST_TEMPLATE.md`
|
||||
- `PULL_REQUEST_TEMPLATE.yaml`
|
||||
- `PULL_REQUEST_TEMPLATE.yml`
|
||||
- `pull_request_template.md`
|
||||
- `pull_request_template.yaml`
|
||||
- `pull_request_template.yml`
|
||||
- `.gitea/PULL_REQUEST_TEMPLATE.md`
|
||||
- `.gitea/PULL_REQUEST_TEMPLATE.yaml`
|
||||
- `.gitea/PULL_REQUEST_TEMPLATE.yml`
|
||||
- `.gitea/pull_request_template.md`
|
||||
- `.gitea/pull_request_template.yaml`
|
||||
- `.gitea/pull_request_template.yml`
|
||||
- `.github/PULL_REQUEST_TEMPLATE.md`
|
||||
- `.github/PULL_REQUEST_TEMPLATE.yaml`
|
||||
- `.github/PULL_REQUEST_TEMPLATE.yml`
|
||||
- `.github/pull_request_template.md`
|
||||
- `.github/pull_request_template.yaml`
|
||||
- `.github/pull_request_template.yml`
|
||||
|
||||
Possible file names for PR default merge message templates:
|
||||
|
||||
- `.gitea/default_merge_message/MERGE_TEMPLATE.md`
|
||||
- `.gitea/default_merge_message/REBASE_TEMPLATE.md`
|
||||
- `.gitea/default_merge_message/REBASE-MERGE_TEMPLATE.md`
|
||||
- `.gitea/default_merge_message/SQUASH_TEMPLATE.md`
|
||||
- `.gitea/default_merge_message/MANUALLY-MERGED_TEMPLATE.md`
|
||||
- `.gitea/default_merge_message/REBASE-UPDATE-ONLY_TEMPLATE.md`
|
||||
|
||||
You can use the following variables enclosed in `${}` inside these templates which follow [os.Expand](https://pkg.go.dev/os#Expand) syntax:
|
||||
|
||||
- BaseRepoOwnerName: Base repository owner name of this pull request
|
||||
- BaseRepoName: Base repository name of this pull request
|
||||
- BaseBranch: Base repository target branch name of this pull request
|
||||
- HeadRepoOwnerName: Head repository owner name of this pull request
|
||||
- HeadRepoName: Head repository name of this pull request
|
||||
- HeadBranch: Head repository branch name of this pull request
|
||||
- PullRequestTitle: Pull request's title
|
||||
- PullRequestDescription: Pull request's description
|
||||
- PullRequestPosterName: Pull request's poster name
|
||||
- PullRequestIndex: Pull request's index number
|
||||
- PullRequestReference: Pull request's reference char with index number. i.e. #1, !2
|
||||
- ClosingIssues: return a string contains all issues which will be closed by this pull request i.e. `close #1, close #2`
|
||||
|
||||
Additionally, the New Issue page URL can be suffixed with `?title=Issue+Title&body=Issue+Text` and the form will be populated with those strings. Those strings will be used instead of the template if there is one.
|
||||
|
||||
## Issue Template Directory
|
||||
## Directory names
|
||||
|
||||
Alternatively, users can create multiple issue templates inside a special directory and allow users to choose one that more specifically
|
||||
addresses their problem.
|
||||
|
@ -85,7 +87,9 @@ Possible directory names for issue templates:
|
|||
- `.gitlab/ISSUE_TEMPLATE`
|
||||
- `.gitlab/issue_template`
|
||||
|
||||
Inside the directory can be multiple markdown (`.md`) issue templates of the form
|
||||
Inside the directory can be multiple markdown (`.md`) or yaml (`.yaml`/`.yml`) issue templates of the form.
|
||||
|
||||
## Syntax for markdown template
|
||||
|
||||
```md
|
||||
---
|
||||
|
@ -108,3 +112,158 @@ In the above example, when a user is presented with the list of issues they can
|
|||
`This template is for testing!`. When submitting an issue with the above example, the issue title would be pre-populated with
|
||||
`[TEST] ` while the issue body would be pre-populated with `This is the template!`. The issue would also be assigned two labels,
|
||||
`bug` and `help needed`, and the issue will have a reference to `main`.
|
||||
|
||||
## Syntax for yaml template
|
||||
|
||||
This example YAML configuration file defines an issue form using several inputs to report a bug.
|
||||
|
||||
```yaml
|
||||
name: Bug Report
|
||||
about: File a bug report
|
||||
title: "[Bug]: "
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
- type: input
|
||||
id: contact
|
||||
attributes:
|
||||
label: Contact Details
|
||||
description: How can we get in touch with you if we need more info?
|
||||
placeholder: ex. email@example.com
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: Also tell us, what did you expect to happen?
|
||||
placeholder: Tell us what you see!
|
||||
value: "A bug happened!"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of our software are you running?
|
||||
options:
|
||||
- 1.0.2 (Default)
|
||||
- 1.0.3 (Edge)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: browsers
|
||||
attributes:
|
||||
label: What browsers are you seeing the problem on?
|
||||
multiple: true
|
||||
options:
|
||||
- Firefox
|
||||
- Chrome
|
||||
- Safari
|
||||
- Microsoft Edge
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Code of Conduct
|
||||
description: By submitting this issue, you agree to follow our [Code of Conduct](https://example.com)
|
||||
options:
|
||||
- label: I agree to follow this project's Code of Conduct
|
||||
required: true
|
||||
```
|
||||
|
||||
### Markdown
|
||||
|
||||
You can use a `markdown` element to display Markdown in your form that provides extra context to the user, but is not submitted.
|
||||
|
||||
Attributes:
|
||||
|
||||
| Key | Description | Required | Type | Default | Valid values |
|
||||
|-------|--------------------------------------------------------------|----------|--------|---------|--------------|
|
||||
| value | The text that is rendered. Markdown formatting is supported. | Required | String | - | - |
|
||||
|
||||
### Textarea
|
||||
|
||||
You can use a `textarea` element to add a multi-line text field to your form. Contributors can also attach files in `textarea` fields.
|
||||
|
||||
Attributes:
|
||||
|
||||
| Key | Description | Required | Type | Default | Valid values |
|
||||
|-------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|--------|--------------|---------------------------|
|
||||
| label | A brief description of the expected user input, which is also displayed in the form. | Required | String | - | - |
|
||||
| description | A description of the text area to provide context or guidance, which is displayed in the form. | Optional | String | Empty String | - |
|
||||
| placeholder | A semi-opaque placeholder that renders in the text area when empty. | Optional | String | Empty String | - |
|
||||
| value | Text that is pre-filled in the text area. | Optional | String | - | - |
|
||||
| render | If a value is provided, submitted text will be formatted into a codeblock. When this key is provided, the text area will not expand for file attachments or Markdown editing. | Optional | String | - | Languages known to Gitea. |
|
||||
|
||||
Validations:
|
||||
|
||||
| Key | Description | Required | Type | Default | Valid values |
|
||||
|----------|------------------------------------------------------|----------|---------|---------|--------------|
|
||||
| required | Prevents form submission until element is completed. | Optional | Boolean | false | - |
|
||||
|
||||
### Input
|
||||
|
||||
You can use an `input` element to add a single-line text field to your form.
|
||||
|
||||
Attributes:
|
||||
|
||||
| Key | Description | Required | Type | Default | Valid values |
|
||||
|-------------|--------------------------------------------------------------------------------------------|----------|--------|--------------|--------------|
|
||||
| label | A brief description of the expected user input, which is also displayed in the form. | Required | String | - | - |
|
||||
| description | A description of the field to provide context or guidance, which is displayed in the form. | Optional | String | Empty String | - |
|
||||
| placeholder | A semi-transparent placeholder that renders in the field when empty. | Optional | String | Empty String | - |
|
||||
| value | Text that is pre-filled in the field. | Optional | String | - | - |
|
||||
|
||||
Validations:
|
||||
|
||||
| Key | Description | Required | Type | Default | Valid values |
|
||||
|-----------|--------------------------------------------------------------------------------------------------|----------|---------|---------|--------------------------------------------------------------------------|
|
||||
| required | Prevents form submission until element is completed. | Optional | Boolean | false | - |
|
||||
| is_number | Prevents form submission until element is filled with a number. | Optional | Boolean | false | - |
|
||||
| regex | Prevents form submission until element is filled with a value that match the regular expression. | Optional | String | - | a [regular expression](https://en.wikipedia.org/wiki/Regular_expression) |
|
||||
|
||||
### Dropdown
|
||||
|
||||
You can use a `dropdown` element to add a dropdown menu in your form.
|
||||
|
||||
Attributes:
|
||||
|
||||
| Key | Description | Required | Type | Default | Valid values |
|
||||
|-------------|-----------------------------------------------------------------------------------------------------|----------|--------------|--------------|--------------|
|
||||
| label | A brief description of the expected user input, which is displayed in the form. | Required | String | - | - |
|
||||
| description | A description of the dropdown to provide extra context or guidance, which is displayed in the form. | Optional | String | Empty String | - |
|
||||
| multiple | Determines if the user can select more than one option. | Optional | Boolean | false | - |
|
||||
| options | An array of options the user can choose from. Cannot be empty and all choices must be distinct. | Required | String array | - | - |
|
||||
|
||||
Validations:
|
||||
|
||||
| Key | Description | Required | Type | Default | Valid values |
|
||||
|----------|------------------------------------------------------|----------|---------|---------|--------------|
|
||||
| required | Prevents form submission until element is completed. | Optional | Boolean | false | - |
|
||||
|
||||
### Checkboxes
|
||||
|
||||
You can use the `checkboxes` element to add a set of checkboxes to your form.
|
||||
|
||||
Attributes:
|
||||
|
||||
| Key | Description | Required | Type | Default | Valid values |
|
||||
|-------------|-------------------------------------------------------------------------------------------------------|----------|--------|--------------|--------------|
|
||||
| label | A brief description of the expected user input, which is displayed in the form. | Required | String | - | - |
|
||||
| description | A description of the set of checkboxes, which is displayed in the form. Supports Markdown formatting. | Optional | String | Empty String | - |
|
||||
| options | An array of checkboxes that the user can select. For syntax, see below. | Required | Array | - | - |
|
||||
|
||||
For each value in the options array, you can set the following keys.
|
||||
|
||||
| Key | Description | Required | Type | Default | Options |
|
||||
|----------|------------------------------------------------------------------------------------------------------------------------------------------|----------|---------|---------|---------|
|
||||
| label | The identifier for the option, which is displayed in the form. Markdown is supported for bold or italic text formatting, and hyperlinks. | Required | String | - | - |
|
||||
| required | Prevents form submission until element is completed. | Optional | Boolean | false | - |
|
||||
|
|
48
docs/content/doc/usage/merge-message-templates.en-us.md
Normal file
48
docs/content/doc/usage/merge-message-templates.en-us.md
Normal file
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
date: "2022-08-31T17:35:40+08:00"
|
||||
title: "Usage: Merge Message templates"
|
||||
slug: "merge-message-templates"
|
||||
weight: 15
|
||||
toc: false
|
||||
draft: false
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "usage"
|
||||
name: "Merge Message templates"
|
||||
weight: 15
|
||||
identifier: "merge-message-templates"
|
||||
---
|
||||
|
||||
# Merge Message templates
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
{{< toc >}}
|
||||
|
||||
## File names
|
||||
|
||||
Possible file names for PR default merge message templates:
|
||||
|
||||
- `.gitea/default_merge_message/MERGE_TEMPLATE.md`
|
||||
- `.gitea/default_merge_message/REBASE_TEMPLATE.md`
|
||||
- `.gitea/default_merge_message/REBASE-MERGE_TEMPLATE.md`
|
||||
- `.gitea/default_merge_message/SQUASH_TEMPLATE.md`
|
||||
- `.gitea/default_merge_message/MANUALLY-MERGED_TEMPLATE.md`
|
||||
- `.gitea/default_merge_message/REBASE-UPDATE-ONLY_TEMPLATE.md`
|
||||
|
||||
## Variables
|
||||
|
||||
You can use the following variables enclosed in `${}` inside these templates which follow [os.Expand](https://pkg.go.dev/os#Expand) syntax:
|
||||
|
||||
- BaseRepoOwnerName: Base repository owner name of this pull request
|
||||
- BaseRepoName: Base repository name of this pull request
|
||||
- BaseBranch: Base repository target branch name of this pull request
|
||||
- HeadRepoOwnerName: Head repository owner name of this pull request
|
||||
- HeadRepoName: Head repository name of this pull request
|
||||
- HeadBranch: Head repository branch name of this pull request
|
||||
- PullRequestTitle: Pull request's title
|
||||
- PullRequestDescription: Pull request's description
|
||||
- PullRequestPosterName: Pull request's poster name
|
||||
- PullRequestIndex: Pull request's index number
|
||||
- PullRequestReference: Pull request's reference char with index number. i.e. #1, !2
|
||||
- ClosingIssues: return a string contains all issues which will be closed by this pull request i.e. `close #1, close #2`
|
185
go.mod
185
go.mod
|
@ -5,50 +5,52 @@ go 1.18
|
|||
require (
|
||||
code.gitea.io/gitea-vet v0.2.2-0.20220122151748-48ebc902541b
|
||||
code.gitea.io/sdk/gitea v0.15.1
|
||||
codeberg.org/gusted/mcaptcha v0.0.0-20220722211632-55c1ffff1222
|
||||
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
|
||||
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb
|
||||
gitea.com/go-chi/cache v0.2.0
|
||||
gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5
|
||||
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8
|
||||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
||||
gitea.com/lunny/levelqueue v0.4.2-0.20220729054728-f020868cc2f7
|
||||
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
github.com/PuerkitoBio/goquery v1.8.0
|
||||
github.com/alecthomas/chroma v0.10.0
|
||||
github.com/blevesearch/bleve/v2 v2.3.2
|
||||
github.com/buildkite/terminal-to-html/v3 v3.6.1
|
||||
github.com/caddyserver/certmagic v0.16.1
|
||||
github.com/buildkite/terminal-to-html/v3 v3.7.0
|
||||
github.com/caddyserver/certmagic v0.17.0
|
||||
github.com/chi-middleware/proxy v1.1.1
|
||||
github.com/denisenkom/go-mssqldb v0.12.0
|
||||
github.com/denisenkom/go-mssqldb v0.12.2
|
||||
github.com/djherbis/buffer v1.2.0
|
||||
github.com/djherbis/nio/v3 v3.0.1
|
||||
github.com/duo-labs/webauthn v0.0.0-20220330035159-03696f3d4499
|
||||
github.com/duo-labs/webauthn v0.0.0-20220815211337-00c9fb5711f5
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/editorconfig/editorconfig-core-go/v2 v2.4.4
|
||||
github.com/editorconfig/editorconfig-core-go/v2 v2.4.5
|
||||
github.com/emirpasic/gods v1.18.1
|
||||
github.com/ethantkoenig/rupture v1.0.1
|
||||
github.com/felixge/fgprof v0.9.2
|
||||
github.com/gliderlabs/ssh v0.3.4
|
||||
github.com/go-ap/activitypub v0.0.0-20220615144428-48208c70483b
|
||||
github.com/felixge/fgprof v0.9.3
|
||||
github.com/fsnotify/fsnotify v1.5.4
|
||||
github.com/gliderlabs/ssh v0.3.5
|
||||
github.com/go-ap/activitypub v0.0.0-20220706134811-0c84d76ce535
|
||||
github.com/go-ap/jsonld v0.0.0-20220615144122-1d862b15410d
|
||||
github.com/go-chi/chi/v5 v5.0.7
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-enry/go-enry/v2 v2.8.2
|
||||
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e
|
||||
github.com/go-git/go-billy/v5 v5.3.1
|
||||
github.com/go-git/go-git/v5 v5.4.3-0.20210630082519-b4368b2a2ca4
|
||||
github.com/go-ldap/ldap/v3 v3.4.3
|
||||
github.com/go-git/go-git/v5 v5.4.3-0.20220529141257-bc1f419cebcf
|
||||
github.com/go-ldap/ldap/v3 v3.4.4
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/go-swagger/go-swagger v0.29.0
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.6.1
|
||||
github.com/go-swagger/go-swagger v0.30.0
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.8.1
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
||||
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
|
||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||
github.com/golang-jwt/jwt/v4 v4.4.1
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2
|
||||
github.com/google/go-github/v45 v45.0.0
|
||||
github.com/google/pprof v0.0.0-20220509035851-59ca7ad80af3
|
||||
github.com/google/pprof v0.0.0-20220829040838-70bd9ae97f40
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/feeds v1.1.1
|
||||
github.com/gorilla/sessions v1.2.1
|
||||
|
@ -59,48 +61,47 @@ require (
|
|||
github.com/json-iterator/go v1.1.12
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
|
||||
github.com/klauspost/compress v1.15.3
|
||||
github.com/klauspost/cpuid/v2 v2.0.12
|
||||
github.com/lib/pq v1.10.5
|
||||
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
||||
github.com/markbates/goth v1.72.0
|
||||
github.com/mattn/go-isatty v0.0.14
|
||||
github.com/mattn/go-sqlite3 v1.14.12
|
||||
github.com/klauspost/compress v1.15.9
|
||||
github.com/klauspost/cpuid/v2 v2.1.1
|
||||
github.com/lib/pq v1.10.6
|
||||
github.com/markbates/goth v1.73.0
|
||||
github.com/mattn/go-isatty v0.0.16
|
||||
github.com/mattn/go-sqlite3 v1.14.13
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/microcosm-cc/bluemonday v1.0.19
|
||||
github.com/minio/minio-go/v7 v7.0.26
|
||||
github.com/minio/minio-go/v7 v7.0.35
|
||||
github.com/msteinert/pam v1.0.0
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||
github.com/niklasfasching/go-org v1.6.2
|
||||
github.com/niklasfasching/go-org v1.6.5
|
||||
github.com/oliamb/cutter v0.2.2
|
||||
github.com/olivere/elastic/v7 v7.0.32
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/prometheus/client_golang v1.12.1
|
||||
github.com/prometheus/client_golang v1.13.0
|
||||
github.com/quasoft/websspi v1.1.2
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.0.0
|
||||
github.com/sergi/go-diff v1.2.0
|
||||
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546
|
||||
github.com/stretchr/testify v1.7.1
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/tstranex/u2f v1.0.0
|
||||
github.com/unrolled/render v1.4.1
|
||||
github.com/unrolled/render v1.5.0
|
||||
github.com/urfave/cli v1.22.9
|
||||
github.com/xanzy/go-gitlab v0.64.0
|
||||
github.com/xanzy/go-gitlab v0.73.1
|
||||
github.com/yohcop/openid-go v1.0.0
|
||||
github.com/yuin/goldmark v1.4.13
|
||||
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594
|
||||
github.com/yuin/goldmark-meta v1.1.0
|
||||
go.jolheiser.com/hcaptcha v0.0.4
|
||||
go.jolheiser.com/pwn v0.0.3
|
||||
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122
|
||||
golang.org/x/net v0.0.0-20220630215102-69896b714898
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
|
||||
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261
|
||||
golang.org/x/text v0.3.7
|
||||
golang.org/x/tools v0.1.10
|
||||
golang.org/x/tools v0.1.12
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/ini.v1 v1.66.4
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
mvdan.cc/xurls/v2 v2.4.0
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
|
||||
|
@ -109,13 +110,14 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.99.0 // indirect
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20200411073322-f0bcc40f0bf2 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e // indirect
|
||||
cloud.google.com/go/compute v1.7.0 // indirect
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.1.1 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20220824120805-4b6e5c587895 // indirect
|
||||
github.com/RoaringBitmap/roaring v0.9.4 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
|
@ -145,6 +147,7 @@ require (
|
|||
github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cloudflare/cfssl v1.6.1 // indirect
|
||||
github.com/cloudflare/circl v1.2.0 // indirect
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490 // indirect
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
|
@ -155,35 +158,33 @@ require (
|
|||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.7.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||
github.com/envoyproxy/go-control-plane v0.10.1 // indirect
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.2 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/fullstorydev/grpcurl v1.8.1 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
|
||||
github.com/go-ap/errors v0.0.0-20220615144307-e8bc4a40ae9f // indirect
|
||||
github.com/go-ap/errors v0.0.0-20220618122732-319f41ac54e1 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
|
||||
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-openapi/analysis v0.21.2 // indirect
|
||||
github.com/go-openapi/errors v0.20.2 // indirect
|
||||
github.com/go-openapi/analysis v0.21.4 // indirect
|
||||
github.com/go-openapi/errors v0.20.3 // indirect
|
||||
github.com/go-openapi/inflect v0.19.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-openapi/loads v0.21.0 // indirect
|
||||
github.com/go-openapi/runtime v0.21.1 // indirect
|
||||
github.com/go-openapi/spec v0.20.4 // indirect
|
||||
github.com/go-openapi/strfmt v0.21.1 // indirect
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/go-openapi/validate v0.20.3 // indirect
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/goccy/go-json v0.9.7 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||
github.com/go-openapi/loads v0.21.2 // indirect
|
||||
github.com/go-openapi/runtime v0.24.1 // indirect
|
||||
github.com/go-openapi/spec v0.20.7 // indirect
|
||||
github.com/go-openapi/strfmt v0.21.3 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-openapi/validate v0.22.0 // indirect
|
||||
github.com/goccy/go-json v0.9.11 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
|
@ -202,7 +203,7 @@ require (
|
|||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jessevdk/go-flags v1.5.0 // indirect
|
||||
|
@ -214,17 +215,18 @@ require (
|
|||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/libdns/libdns v0.2.1 // indirect
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/markbates/going v1.0.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mholt/acmez v1.0.2 // indirect
|
||||
github.com/miekg/dns v1.1.48 // indirect
|
||||
github.com/mholt/acmez v1.0.4 // indirect
|
||||
github.com/miekg/dns v1.1.50 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
||||
|
@ -232,69 +234,68 @@ require (
|
|||
github.com/nwaples/rardecode v1.1.3 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.1 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/rivo/uniseg v0.3.4 // indirect
|
||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||
github.com/rs/xid v1.4.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/soheilhy/cmux v0.1.5 // indirect
|
||||
github.com/spf13/afero v1.8.0 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/cobra v1.3.0 // indirect
|
||||
github.com/spf13/afero v1.8.2 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/cobra v1.5.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.10.1 // indirect
|
||||
github.com/spf13/viper v1.12.0 // indirect
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/subosito/gotenv v1.3.0 // indirect
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
|
||||
github.com/toqueteos/webbrowser v1.2.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||
github.com/unknwon/com v1.0.1 // indirect
|
||||
github.com/valyala/fastjson v1.6.3 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.1 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.2 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.1 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.1 // indirect
|
||||
go.etcd.io/etcd/client/v2 v2.305.1 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.4 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.4 // indirect
|
||||
go.etcd.io/etcd/client/v2 v2.305.4 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.4 // indirect
|
||||
go.etcd.io/etcd/etcdctl/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/raft/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/server/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/tests/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/v3 v3.5.0-alpha.0 // indirect
|
||||
go.mongodb.org/mongo-driver v1.8.2 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.mongodb.org/mongo-driver v1.10.1 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
|
||||
go.uber.org/zap v1.23.0 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
|
||||
google.golang.org/grpc v1.43.0 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90 // indirect
|
||||
google.golang.org/grpc v1.47.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
|
||||
|
||||
replace github.com/markbates/goth v1.68.0 => github.com/zeripath/goth v1.68.1-0.20220109111530-754359885dce
|
||||
|
||||
replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0
|
||||
|
||||
replace github.com/satori/go.uuid v1.2.0 => github.com/gofrs/uuid v4.2.0+incompatible
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export default {
|
||||
rootDir: 'web_src',
|
||||
setupFilesAfterEnv: ['jest-extended/all'],
|
||||
testEnvironment: '@happy-dom/jest-environment',
|
||||
testEnvironment: 'jest-environment-jsdom',
|
||||
testMatch: ['<rootDir>/**/*.test.js'],
|
||||
testTimeout: 20000,
|
||||
transform: {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
package activities
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -98,7 +98,14 @@ func (a *Action) TableIndices() []*schemas.Index {
|
|||
actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
|
||||
actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")
|
||||
|
||||
return []*schemas.Index{actUserIndex, repoIndex}
|
||||
indices := []*schemas.Index{actUserIndex, repoIndex}
|
||||
if setting.Database.UsePostgreSQL {
|
||||
cudIndex := schemas.NewIndex("c_u_d", schemas.IndexType)
|
||||
cudIndex.AddColumn("created_unix", "user_id", "is_deleted")
|
||||
indices = append(indices, cudIndex)
|
||||
}
|
||||
|
||||
return indices
|
||||
}
|
||||
|
||||
// GetOpType gets the ActionType of this action.
|
||||
|
@ -211,21 +218,6 @@ func (a *Action) GetRepoLink() string {
|
|||
return path.Join(setting.AppSubURL, "/", url.PathEscape(a.GetRepoUserName()), url.PathEscape(a.GetRepoName()))
|
||||
}
|
||||
|
||||
// GetRepositoryFromMatch returns a *repo_model.Repository from a username and repo strings
|
||||
func GetRepositoryFromMatch(ownerName, repoName string) (*repo_model.Repository, error) {
|
||||
var err error
|
||||
refRepo, err := repo_model.GetRepositoryByOwnerAndName(ownerName, repoName)
|
||||
if err != nil {
|
||||
if repo_model.IsErrRepoNotExist(err) {
|
||||
log.Warn("Repository referenced in commit but does not exist: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
log.Error("repo_model.GetRepositoryByOwnerAndName: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return refRepo, nil
|
||||
}
|
||||
|
||||
// GetCommentLink returns link to action comment.
|
||||
func (a *Action) GetCommentLink() string {
|
||||
return a.getCommentLink(db.DefaultContext)
|
||||
|
@ -275,7 +267,7 @@ func (a *Action) GetRefLink() string {
|
|||
return a.GetRepoLink() + "/src/branch/" + util.PathEscapeSegments(strings.TrimPrefix(a.RefName, git.BranchPrefix))
|
||||
case strings.HasPrefix(a.RefName, git.TagPrefix):
|
||||
return a.GetRepoLink() + "/src/tag/" + util.PathEscapeSegments(strings.TrimPrefix(a.RefName, git.TagPrefix))
|
||||
case len(a.RefName) == 40 && git.SHAPattern.MatchString(a.RefName):
|
||||
case len(a.RefName) == 40 && git.IsValidSHAPattern(a.RefName):
|
||||
return a.GetRepoLink() + "/src/commit/" + a.RefName
|
||||
default:
|
||||
// FIXME: we will just assume it's a branch - this was the old way - at some point we may want to enforce that there is always a ref here.
|
||||
|
@ -372,7 +364,8 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, error) {
|
|||
return actions, nil
|
||||
}
|
||||
|
||||
func activityReadable(user, doer *user_model.User) bool {
|
||||
// ActivityReadable return whether doer can read activities of user
|
||||
func ActivityReadable(user, doer *user_model.User) bool {
|
||||
return !user.KeepActivityPrivate ||
|
||||
doer != nil && (doer.IsAdmin || user.ID == doer.ID)
|
||||
}
|
||||
|
@ -602,3 +595,23 @@ func DeleteIssueActions(ctx context.Context, repoID, issueID int64) error {
|
|||
Delete(&Action{})
|
||||
return err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
package activities
|
||||
|
||||
import (
|
||||
"context"
|
|
@ -2,12 +2,13 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
package activities_test
|
||||
|
||||
import (
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
@ -21,7 +22,7 @@ func TestAction_GetRepoPath(t *testing.T) {
|
|||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{})
|
||||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||
action := &Action{RepoID: repo.ID}
|
||||
action := &activities_model.Action{RepoID: repo.ID}
|
||||
assert.Equal(t, path.Join(owner.Name, repo.Name), action.GetRepoPath())
|
||||
}
|
||||
|
||||
|
@ -29,7 +30,7 @@ func TestAction_GetRepoLink(t *testing.T) {
|
|||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{})
|
||||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||
action := &Action{RepoID: repo.ID}
|
||||
action := &activities_model.Action{RepoID: repo.ID}
|
||||
setting.AppSubURL = "/suburl"
|
||||
expected := path.Join(setting.AppSubURL, owner.Name, repo.Name)
|
||||
assert.Equal(t, expected, action.GetRepoLink())
|
||||
|
@ -40,7 +41,7 @@ func TestGetFeeds(t *testing.T) {
|
|||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
|
||||
actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
|
||||
actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedUser: user,
|
||||
Actor: user,
|
||||
IncludePrivate: true,
|
||||
|
@ -53,7 +54,7 @@ func TestGetFeeds(t *testing.T) {
|
|||
assert.EqualValues(t, user.ID, actions[0].UserID)
|
||||
}
|
||||
|
||||
actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
|
||||
actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedUser: user,
|
||||
Actor: user,
|
||||
IncludePrivate: false,
|
||||
|
@ -70,7 +71,7 @@ func TestGetFeedsForRepos(t *testing.T) {
|
|||
pubRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 8})
|
||||
|
||||
// private repo & no login
|
||||
actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
|
||||
actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedRepo: privRepo,
|
||||
IncludePrivate: true,
|
||||
})
|
||||
|
@ -78,7 +79,7 @@ func TestGetFeedsForRepos(t *testing.T) {
|
|||
assert.Len(t, actions, 0)
|
||||
|
||||
// public repo & no login
|
||||
actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
|
||||
actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedRepo: pubRepo,
|
||||
IncludePrivate: true,
|
||||
})
|
||||
|
@ -86,7 +87,7 @@ func TestGetFeedsForRepos(t *testing.T) {
|
|||
assert.Len(t, actions, 1)
|
||||
|
||||
// private repo and login
|
||||
actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
|
||||
actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedRepo: privRepo,
|
||||
IncludePrivate: true,
|
||||
Actor: user,
|
||||
|
@ -95,7 +96,7 @@ func TestGetFeedsForRepos(t *testing.T) {
|
|||
assert.Len(t, actions, 1)
|
||||
|
||||
// public repo & login
|
||||
actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
|
||||
actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedRepo: pubRepo,
|
||||
IncludePrivate: true,
|
||||
Actor: user,
|
||||
|
@ -110,7 +111,7 @@ func TestGetFeeds2(t *testing.T) {
|
|||
org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
|
||||
actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
|
||||
actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedUser: org,
|
||||
Actor: user,
|
||||
IncludePrivate: true,
|
||||
|
@ -124,7 +125,7 @@ func TestGetFeeds2(t *testing.T) {
|
|||
assert.EqualValues(t, org.ID, actions[0].UserID)
|
||||
}
|
||||
|
||||
actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
|
||||
actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedUser: org,
|
||||
Actor: user,
|
||||
IncludePrivate: false,
|
||||
|
@ -171,40 +172,40 @@ func TestActivityReadable(t *testing.T) {
|
|||
result: true,
|
||||
}}
|
||||
for _, test := range tt {
|
||||
assert.Equal(t, test.result, activityReadable(test.user, test.doer), test.desc)
|
||||
assert.Equal(t, test.result, activities_model.ActivityReadable(test.user, test.doer), test.desc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotifyWatchers(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
action := &Action{
|
||||
action := &activities_model.Action{
|
||||
ActUserID: 8,
|
||||
RepoID: 1,
|
||||
OpType: ActionStarRepo,
|
||||
OpType: activities_model.ActionStarRepo,
|
||||
}
|
||||
assert.NoError(t, NotifyWatchers(action))
|
||||
assert.NoError(t, activities_model.NotifyWatchers(action))
|
||||
|
||||
// One watchers are inactive, thus action is only created for user 8, 1, 4, 11
|
||||
unittest.AssertExistsAndLoadBean(t, &Action{
|
||||
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
|
||||
ActUserID: action.ActUserID,
|
||||
UserID: 8,
|
||||
RepoID: action.RepoID,
|
||||
OpType: action.OpType,
|
||||
})
|
||||
unittest.AssertExistsAndLoadBean(t, &Action{
|
||||
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
|
||||
ActUserID: action.ActUserID,
|
||||
UserID: 1,
|
||||
RepoID: action.RepoID,
|
||||
OpType: action.OpType,
|
||||
})
|
||||
unittest.AssertExistsAndLoadBean(t, &Action{
|
||||
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
|
||||
ActUserID: action.ActUserID,
|
||||
UserID: 4,
|
||||
RepoID: action.RepoID,
|
||||
OpType: action.OpType,
|
||||
})
|
||||
unittest.AssertExistsAndLoadBean(t, &Action{
|
||||
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
|
||||
ActUserID: action.ActUserID,
|
||||
UserID: 11,
|
||||
RepoID: action.RepoID,
|
||||
|
@ -215,12 +216,12 @@ func TestNotifyWatchers(t *testing.T) {
|
|||
func TestGetFeedsCorrupted(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &Action{
|
||||
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
|
||||
ID: 8,
|
||||
RepoID: 1700,
|
||||
})
|
||||
|
||||
actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
|
||||
actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedUser: user,
|
||||
Actor: user,
|
||||
IncludePrivate: true,
|
||||
|
@ -235,12 +236,12 @@ func TestConsistencyUpdateAction(t *testing.T) {
|
|||
}
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
id := 8
|
||||
unittest.AssertExistsAndLoadBean(t, &Action{
|
||||
unittest.AssertExistsAndLoadBean(t, &activities_model.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)
|
||||
actions := make([]*activities_model.Action, 0, 1)
|
||||
//
|
||||
// XORM returns an error when created_unix is a string
|
||||
//
|
||||
|
@ -251,17 +252,17 @@ func TestConsistencyUpdateAction(t *testing.T) {
|
|||
//
|
||||
// Get rid of incorrectly set created_unix
|
||||
//
|
||||
count, err := CountActionCreatedUnixString()
|
||||
count, err := activities_model.CountActionCreatedUnixString()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, count)
|
||||
count, err = FixActionCreatedUnixString()
|
||||
count, err = activities_model.FixActionCreatedUnixString()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, count)
|
||||
|
||||
count, err = CountActionCreatedUnixString()
|
||||
count, err = activities_model.CountActionCreatedUnixString()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 0, count)
|
||||
count, err = FixActionCreatedUnixString()
|
||||
count, err = activities_model.FixActionCreatedUnixString()
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 0, count)
|
||||
|
||||
|
@ -269,5 +270,5 @@ func TestConsistencyUpdateAction(t *testing.T) {
|
|||
// XORM must be happy now
|
||||
//
|
||||
assert.NoError(t, db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions))
|
||||
unittest.CheckConsistencyFor(t, &Action{})
|
||||
unittest.CheckConsistencyFor(t, &activities_model.Action{})
|
||||
}
|
20
models/activities/main_test.go
Normal file
20
models/activities/main_test.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright 2020 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 activities_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
_ "code.gitea.io/gitea/models"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m, &unittest.TestOptions{
|
||||
GiteaRootPath: filepath.Join("..", ".."),
|
||||
})
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
package activities
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -13,6 +13,7 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
@ -267,10 +268,10 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
|
|||
|
||||
return err
|
||||
}
|
||||
if issue.IsPull && !CheckRepoUnitUser(ctx, issue.Repo, user, unit.TypePullRequests) {
|
||||
if issue.IsPull && !access_model.CheckRepoUnitUser(ctx, issue.Repo, user, unit.TypePullRequests) {
|
||||
continue
|
||||
}
|
||||
if !issue.IsPull && !CheckRepoUnitUser(ctx, issue.Repo, user, unit.TypeIssues) {
|
||||
if !issue.IsPull && !access_model.CheckRepoUnitUser(ctx, issue.Repo, user, unit.TypeIssues) {
|
||||
continue
|
||||
}
|
||||
|
|
@ -2,11 +2,12 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
package activities_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
@ -19,22 +20,22 @@ func TestCreateOrUpdateIssueNotifications(t *testing.T) {
|
|||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
|
||||
|
||||
assert.NoError(t, CreateOrUpdateIssueNotifications(issue.ID, 0, 2, 0))
|
||||
assert.NoError(t, activities_model.CreateOrUpdateIssueNotifications(issue.ID, 0, 2, 0))
|
||||
|
||||
// User 9 is inactive, thus notifications for user 1 and 4 are created
|
||||
notf := unittest.AssertExistsAndLoadBean(t, &Notification{UserID: 1, IssueID: issue.ID})
|
||||
assert.Equal(t, NotificationStatusUnread, notf.Status)
|
||||
notf := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{UserID: 1, IssueID: issue.ID})
|
||||
assert.Equal(t, activities_model.NotificationStatusUnread, notf.Status)
|
||||
unittest.CheckConsistencyFor(t, &issues_model.Issue{ID: issue.ID})
|
||||
|
||||
notf = unittest.AssertExistsAndLoadBean(t, &Notification{UserID: 4, IssueID: issue.ID})
|
||||
assert.Equal(t, NotificationStatusUnread, notf.Status)
|
||||
notf = unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{UserID: 4, IssueID: issue.ID})
|
||||
assert.Equal(t, activities_model.NotificationStatusUnread, notf.Status)
|
||||
}
|
||||
|
||||
func TestNotificationsForUser(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
statuses := []NotificationStatus{NotificationStatusRead, NotificationStatusUnread}
|
||||
notfs, err := NotificationsForUser(db.DefaultContext, user, statuses, 1, 10)
|
||||
statuses := []activities_model.NotificationStatus{activities_model.NotificationStatusRead, activities_model.NotificationStatusUnread}
|
||||
notfs, err := activities_model.NotificationsForUser(db.DefaultContext, user, statuses, 1, 10)
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, notfs, 3) {
|
||||
assert.EqualValues(t, 5, notfs[0].ID)
|
||||
|
@ -48,7 +49,7 @@ func TestNotificationsForUser(t *testing.T) {
|
|||
|
||||
func TestNotification_GetRepo(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
notf := unittest.AssertExistsAndLoadBean(t, &Notification{RepoID: 1})
|
||||
notf := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{RepoID: 1})
|
||||
repo, err := notf.GetRepo()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, repo, notf.Repository)
|
||||
|
@ -57,7 +58,7 @@ func TestNotification_GetRepo(t *testing.T) {
|
|||
|
||||
func TestNotification_GetIssue(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
notf := unittest.AssertExistsAndLoadBean(t, &Notification{RepoID: 1})
|
||||
notf := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{RepoID: 1})
|
||||
issue, err := notf.GetIssue()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, issue, notf.Issue)
|
||||
|
@ -67,11 +68,11 @@ func TestNotification_GetIssue(t *testing.T) {
|
|||
func TestGetNotificationCount(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
cnt, err := GetNotificationCount(db.DefaultContext, user, NotificationStatusRead)
|
||||
cnt, err := activities_model.GetNotificationCount(db.DefaultContext, user, activities_model.NotificationStatusRead)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 0, cnt)
|
||||
|
||||
cnt, err = GetNotificationCount(db.DefaultContext, user, NotificationStatusUnread)
|
||||
cnt, err = activities_model.GetNotificationCount(db.DefaultContext, user, activities_model.NotificationStatusUnread)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
}
|
||||
|
@ -80,15 +81,15 @@ func TestSetNotificationStatus(t *testing.T) {
|
|||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
notf := unittest.AssertExistsAndLoadBean(t,
|
||||
&Notification{UserID: user.ID, Status: NotificationStatusRead})
|
||||
_, err := SetNotificationStatus(notf.ID, user, NotificationStatusPinned)
|
||||
&activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusRead})
|
||||
_, err := activities_model.SetNotificationStatus(notf.ID, user, activities_model.NotificationStatusPinned)
|
||||
assert.NoError(t, err)
|
||||
unittest.AssertExistsAndLoadBean(t,
|
||||
&Notification{ID: notf.ID, Status: NotificationStatusPinned})
|
||||
&activities_model.Notification{ID: notf.ID, Status: activities_model.NotificationStatusPinned})
|
||||
|
||||
_, err = SetNotificationStatus(1, user, NotificationStatusRead)
|
||||
_, err = activities_model.SetNotificationStatus(1, user, activities_model.NotificationStatusRead)
|
||||
assert.Error(t, err)
|
||||
_, err = SetNotificationStatus(unittest.NonexistentID, user, NotificationStatusRead)
|
||||
_, err = activities_model.SetNotificationStatus(unittest.NonexistentID, user, activities_model.NotificationStatusRead)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
|
@ -96,16 +97,16 @@ func TestUpdateNotificationStatuses(t *testing.T) {
|
|||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
notfUnread := unittest.AssertExistsAndLoadBean(t,
|
||||
&Notification{UserID: user.ID, Status: NotificationStatusUnread})
|
||||
&activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusUnread})
|
||||
notfRead := unittest.AssertExistsAndLoadBean(t,
|
||||
&Notification{UserID: user.ID, Status: NotificationStatusRead})
|
||||
&activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusRead})
|
||||
notfPinned := unittest.AssertExistsAndLoadBean(t,
|
||||
&Notification{UserID: user.ID, Status: NotificationStatusPinned})
|
||||
assert.NoError(t, UpdateNotificationStatuses(user, NotificationStatusUnread, NotificationStatusRead))
|
||||
&activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusPinned})
|
||||
assert.NoError(t, activities_model.UpdateNotificationStatuses(user, activities_model.NotificationStatusUnread, activities_model.NotificationStatusRead))
|
||||
unittest.AssertExistsAndLoadBean(t,
|
||||
&Notification{ID: notfUnread.ID, Status: NotificationStatusRead})
|
||||
&activities_model.Notification{ID: notfUnread.ID, Status: activities_model.NotificationStatusRead})
|
||||
unittest.AssertExistsAndLoadBean(t,
|
||||
&Notification{ID: notfRead.ID, Status: NotificationStatusRead})
|
||||
&activities_model.Notification{ID: notfRead.ID, Status: activities_model.NotificationStatusRead})
|
||||
unittest.AssertExistsAndLoadBean(t,
|
||||
&Notification{ID: notfPinned.ID, Status: NotificationStatusPinned})
|
||||
&activities_model.Notification{ID: notfPinned.ID, Status: activities_model.NotificationStatusPinned})
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
package activities
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -39,7 +39,7 @@ type ActivityStats struct {
|
|||
ClosedIssues issues_model.IssueList
|
||||
ClosedIssueAuthorCount int64
|
||||
UnresolvedIssues issues_model.IssueList
|
||||
PublishedReleases []*Release
|
||||
PublishedReleases []*repo_model.Release
|
||||
PublishedReleaseAuthorCount int64
|
||||
Code *git.CodeActivityStats
|
||||
}
|
||||
|
@ -344,7 +344,7 @@ func (stats *ActivityStats) FillReleases(repoID int64, fromTime time.Time) error
|
|||
// Published releases list
|
||||
sess := releasesForActivityStatement(repoID, fromTime)
|
||||
sess.OrderBy("release.created_unix DESC")
|
||||
stats.PublishedReleases = make([]*Release, 0)
|
||||
stats.PublishedReleases = make([]*repo_model.Release, 0)
|
||||
if err = sess.Find(&stats.PublishedReleases); err != nil {
|
||||
return err
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
package activities
|
||||
|
||||
import (
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
|
@ -101,7 +101,7 @@ func GetStatistic() (stats Statistic) {
|
|||
stats.Counter.Oauth = 0
|
||||
stats.Counter.Follow, _ = e.Count(new(user_model.Follow))
|
||||
stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror))
|
||||
stats.Counter.Release, _ = e.Count(new(Release))
|
||||
stats.Counter.Release, _ = e.Count(new(repo_model.Release))
|
||||
stats.Counter.AuthSource = auth.CountSources()
|
||||
stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook))
|
||||
stats.Counter.Milestone, _ = e.Count(new(issues_model.Milestone))
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.package models
|
||||
|
||||
package models
|
||||
package activities
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
|
@ -31,7 +31,7 @@ func GetUserHeatmapDataByUserTeam(user *user_model.User, team *organization.Team
|
|||
func getUserHeatmapData(user *user_model.User, team *organization.Team, doer *user_model.User) ([]*UserHeatmapData, error) {
|
||||
hdata := make([]*UserHeatmapData, 0)
|
||||
|
||||
if !activityReadable(user, doer) {
|
||||
if !ActivityReadable(user, doer) {
|
||||
return hdata, nil
|
||||
}
|
||||
|
|
@ -2,13 +2,14 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.package models
|
||||
|
||||
package models
|
||||
package activities_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
@ -73,7 +74,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
|
|||
}
|
||||
|
||||
// get the action for comparison
|
||||
actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
|
||||
actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedUser: user,
|
||||
Actor: doer,
|
||||
IncludePrivate: true,
|
||||
|
@ -83,7 +84,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// Get the heatmap and compare
|
||||
heatmap, err := GetUserHeatmapDataByUser(user, doer)
|
||||
heatmap, err := activities_model.GetUserHeatmapDataByUser(user, doer)
|
||||
var contributions int
|
||||
for _, hm := range heatmap {
|
||||
contributions += int(hm.Contributions)
|
|
@ -2,18 +2,21 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package admin
|
||||
package admin_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
_ "code.gitea.io/gitea/models"
|
||||
_ "code.gitea.io/gitea/models/activities"
|
||||
_ "code.gitea.io/gitea/models/perm/access"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m, &unittest.TestOptions{
|
||||
GiteaRootPath: filepath.Join("..", ".."),
|
||||
FixtureFiles: []string{"notice.yml"},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package admin
|
||||
package admin_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/admin"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
|
@ -14,8 +15,8 @@ import (
|
|||
)
|
||||
|
||||
func TestNotice_TrStr(t *testing.T) {
|
||||
notice := &Notice{
|
||||
Type: NoticeRepository,
|
||||
notice := &admin.Notice{
|
||||
Type: admin.NoticeRepository,
|
||||
Description: "test description",
|
||||
}
|
||||
assert.Equal(t, "admin.notices.type_1", notice.TrStr())
|
||||
|
@ -24,24 +25,24 @@ func TestNotice_TrStr(t *testing.T) {
|
|||
func TestCreateNotice(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
noticeBean := &Notice{
|
||||
Type: NoticeRepository,
|
||||
noticeBean := &admin.Notice{
|
||||
Type: admin.NoticeRepository,
|
||||
Description: "test description",
|
||||
}
|
||||
unittest.AssertNotExistsBean(t, noticeBean)
|
||||
assert.NoError(t, CreateNotice(db.DefaultContext, noticeBean.Type, noticeBean.Description))
|
||||
assert.NoError(t, admin.CreateNotice(db.DefaultContext, noticeBean.Type, noticeBean.Description))
|
||||
unittest.AssertExistsAndLoadBean(t, noticeBean)
|
||||
}
|
||||
|
||||
func TestCreateRepositoryNotice(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
noticeBean := &Notice{
|
||||
Type: NoticeRepository,
|
||||
noticeBean := &admin.Notice{
|
||||
Type: admin.NoticeRepository,
|
||||
Description: "test description",
|
||||
}
|
||||
unittest.AssertNotExistsBean(t, noticeBean)
|
||||
assert.NoError(t, CreateRepositoryNotice(noticeBean.Description))
|
||||
assert.NoError(t, admin.CreateRepositoryNotice(noticeBean.Description))
|
||||
unittest.AssertExistsAndLoadBean(t, noticeBean)
|
||||
}
|
||||
|
||||
|
@ -49,20 +50,20 @@ func TestCreateRepositoryNotice(t *testing.T) {
|
|||
|
||||
func TestCountNotices(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
assert.Equal(t, int64(3), CountNotices())
|
||||
assert.Equal(t, int64(3), admin.CountNotices())
|
||||
}
|
||||
|
||||
func TestNotices(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
notices, err := Notices(1, 2)
|
||||
notices, err := admin.Notices(1, 2)
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, notices, 2) {
|
||||
assert.Equal(t, int64(3), notices[0].ID)
|
||||
assert.Equal(t, int64(2), notices[1].ID)
|
||||
}
|
||||
|
||||
notices, err = Notices(2, 2)
|
||||
notices, err = admin.Notices(2, 2)
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, notices, 1) {
|
||||
assert.Equal(t, int64(1), notices[0].ID)
|
||||
|
@ -72,45 +73,45 @@ func TestNotices(t *testing.T) {
|
|||
func TestDeleteNotice(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
|
||||
assert.NoError(t, DeleteNotice(3))
|
||||
unittest.AssertNotExistsBean(t, &Notice{ID: 3})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
|
||||
assert.NoError(t, admin.DeleteNotice(3))
|
||||
unittest.AssertNotExistsBean(t, &admin.Notice{ID: 3})
|
||||
}
|
||||
|
||||
func TestDeleteNotices(t *testing.T) {
|
||||
// delete a non-empty range
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
unittest.AssertExistsAndLoadBean(t, &Notice{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &Notice{ID: 2})
|
||||
unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
|
||||
assert.NoError(t, DeleteNotices(1, 2))
|
||||
unittest.AssertNotExistsBean(t, &Notice{ID: 1})
|
||||
unittest.AssertNotExistsBean(t, &Notice{ID: 2})
|
||||
unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
|
||||
assert.NoError(t, admin.DeleteNotices(1, 2))
|
||||
unittest.AssertNotExistsBean(t, &admin.Notice{ID: 1})
|
||||
unittest.AssertNotExistsBean(t, &admin.Notice{ID: 2})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
|
||||
}
|
||||
|
||||
func TestDeleteNotices2(t *testing.T) {
|
||||
// delete an empty range
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
unittest.AssertExistsAndLoadBean(t, &Notice{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &Notice{ID: 2})
|
||||
unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
|
||||
assert.NoError(t, DeleteNotices(3, 2))
|
||||
unittest.AssertExistsAndLoadBean(t, &Notice{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &Notice{ID: 2})
|
||||
unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
|
||||
assert.NoError(t, admin.DeleteNotices(3, 2))
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
|
||||
}
|
||||
|
||||
func TestDeleteNoticesByIDs(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
unittest.AssertExistsAndLoadBean(t, &Notice{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &Notice{ID: 2})
|
||||
unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
|
||||
assert.NoError(t, DeleteNoticesByIDs([]int64{1, 3}))
|
||||
unittest.AssertNotExistsBean(t, &Notice{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &Notice{ID: 2})
|
||||
unittest.AssertNotExistsBean(t, &Notice{ID: 3})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
|
||||
assert.NoError(t, admin.DeleteNoticesByIDs([]int64{1, 3}))
|
||||
unittest.AssertNotExistsBean(t, &admin.Notice{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
|
||||
unittest.AssertNotExistsBean(t, &admin.Notice{ID: 3})
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
|
@ -33,7 +33,7 @@ type GPGKey struct {
|
|||
OwnerID int64 `xorm:"INDEX NOT NULL"`
|
||||
KeyID string `xorm:"INDEX CHAR(16) NOT NULL"`
|
||||
PrimaryKeyID string `xorm:"CHAR(16)"`
|
||||
Content string `xorm:"TEXT NOT NULL"`
|
||||
Content string `xorm:"MEDIUMTEXT NOT NULL"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||
ExpiredUnix timeutil.TimeStamp
|
||||
AddedUnix timeutil.TimeStamp
|
||||
|
@ -63,6 +63,15 @@ func (key *GPGKey) AfterLoad(session *xorm.Session) {
|
|||
}
|
||||
}
|
||||
|
||||
// PaddedKeyID show KeyID padded to 16 characters
|
||||
func (key *GPGKey) PaddedKeyID() string {
|
||||
if len(key.KeyID) > 15 {
|
||||
return key.KeyID
|
||||
}
|
||||
zeros := "0000000000000000"
|
||||
return zeros[0:16-len(key.KeyID)] + key.KeyID
|
||||
}
|
||||
|
||||
// ListGPGKeys returns a list of public keys belongs to given user.
|
||||
func ListGPGKeys(ctx context.Context, uid int64, listOptions db.ListOptions) ([]*GPGKey, error) {
|
||||
sess := db.GetEngine(ctx).Table(&GPGKey{}).Where("owner_id=? AND primary_key_id=''", uid)
|
||||
|
|
|
@ -41,7 +41,7 @@ type PublicKey struct {
|
|||
OwnerID int64 `xorm:"INDEX NOT NULL"`
|
||||
Name string `xorm:"NOT NULL"`
|
||||
Fingerprint string `xorm:"INDEX NOT NULL"`
|
||||
Content string `xorm:"TEXT NOT NULL"`
|
||||
Content string `xorm:"MEDIUMTEXT NOT NULL"`
|
||||
Mode perm.AccessMode `xorm:"NOT NULL DEFAULT 2"`
|
||||
Type KeyType `xorm:"NOT NULL DEFAULT 1"`
|
||||
LoginSourceID int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
|
|
|
@ -2,24 +2,22 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package auth
|
||||
package auth_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
_ "code.gitea.io/gitea/models"
|
||||
_ "code.gitea.io/gitea/models/activities"
|
||||
_ "code.gitea.io/gitea/models/auth"
|
||||
_ "code.gitea.io/gitea/models/perm/access"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m, &unittest.TestOptions{
|
||||
GiteaRootPath: filepath.Join("..", ".."),
|
||||
FixtureFiles: []string{
|
||||
"login_source.yml",
|
||||
"oauth2_application.yml",
|
||||
"oauth2_authorization_code.yml",
|
||||
"oauth2_grant.yml",
|
||||
"webauthn_credential.yml",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package auth
|
||||
package auth_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
|
@ -17,23 +18,23 @@ import (
|
|||
|
||||
func TestOAuth2Application_GenerateClientSecret(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
app := unittest.AssertExistsAndLoadBean(t, &OAuth2Application{ID: 1})
|
||||
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
|
||||
secret, err := app.GenerateClientSecret()
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, len(secret) > 0)
|
||||
unittest.AssertExistsAndLoadBean(t, &OAuth2Application{ID: 1, ClientSecret: app.ClientSecret})
|
||||
unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1, ClientSecret: app.ClientSecret})
|
||||
}
|
||||
|
||||
func BenchmarkOAuth2Application_GenerateClientSecret(b *testing.B) {
|
||||
assert.NoError(b, unittest.PrepareTestDatabase())
|
||||
app := unittest.AssertExistsAndLoadBean(b, &OAuth2Application{ID: 1})
|
||||
app := unittest.AssertExistsAndLoadBean(b, &auth_model.OAuth2Application{ID: 1})
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = app.GenerateClientSecret()
|
||||
}
|
||||
}
|
||||
|
||||
func TestOAuth2Application_ContainsRedirectURI(t *testing.T) {
|
||||
app := &OAuth2Application{
|
||||
app := &auth_model.OAuth2Application{
|
||||
RedirectURIs: []string{"a", "b", "c"},
|
||||
}
|
||||
assert.True(t, app.ContainsRedirectURI("a"))
|
||||
|
@ -44,7 +45,7 @@ func TestOAuth2Application_ContainsRedirectURI(t *testing.T) {
|
|||
|
||||
func TestOAuth2Application_ValidateClientSecret(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
app := unittest.AssertExistsAndLoadBean(t, &OAuth2Application{ID: 1})
|
||||
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
|
||||
secret, err := app.GenerateClientSecret()
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, app.ValidateClientSecret([]byte(secret)))
|
||||
|
@ -53,31 +54,31 @@ func TestOAuth2Application_ValidateClientSecret(t *testing.T) {
|
|||
|
||||
func TestGetOAuth2ApplicationByClientID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
app, err := GetOAuth2ApplicationByClientID(db.DefaultContext, "da7da3ba-9a13-4167-856f-3899de0b0138")
|
||||
app, err := auth_model.GetOAuth2ApplicationByClientID(db.DefaultContext, "da7da3ba-9a13-4167-856f-3899de0b0138")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "da7da3ba-9a13-4167-856f-3899de0b0138", app.ClientID)
|
||||
|
||||
app, err = GetOAuth2ApplicationByClientID(db.DefaultContext, "invalid client id")
|
||||
app, err = auth_model.GetOAuth2ApplicationByClientID(db.DefaultContext, "invalid client id")
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, app)
|
||||
}
|
||||
|
||||
func TestCreateOAuth2Application(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
app, err := CreateOAuth2Application(db.DefaultContext, CreateOAuth2ApplicationOptions{Name: "newapp", UserID: 1})
|
||||
app, err := auth_model.CreateOAuth2Application(db.DefaultContext, auth_model.CreateOAuth2ApplicationOptions{Name: "newapp", UserID: 1})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "newapp", app.Name)
|
||||
assert.Len(t, app.ClientID, 36)
|
||||
unittest.AssertExistsAndLoadBean(t, &OAuth2Application{Name: "newapp"})
|
||||
unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{Name: "newapp"})
|
||||
}
|
||||
|
||||
func TestOAuth2Application_TableName(t *testing.T) {
|
||||
assert.Equal(t, "oauth2_application", new(OAuth2Application).TableName())
|
||||
assert.Equal(t, "oauth2_application", new(auth_model.OAuth2Application).TableName())
|
||||
}
|
||||
|
||||
func TestOAuth2Application_GetGrantByUserID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
app := unittest.AssertExistsAndLoadBean(t, &OAuth2Application{ID: 1})
|
||||
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
|
||||
grant, err := app.GetGrantByUserID(db.DefaultContext, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), grant.UserID)
|
||||
|
@ -89,7 +90,7 @@ func TestOAuth2Application_GetGrantByUserID(t *testing.T) {
|
|||
|
||||
func TestOAuth2Application_CreateGrant(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
app := unittest.AssertExistsAndLoadBean(t, &OAuth2Application{ID: 1})
|
||||
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
|
||||
grant, err := app.CreateGrant(db.DefaultContext, 2, "")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, grant)
|
||||
|
@ -102,26 +103,26 @@ func TestOAuth2Application_CreateGrant(t *testing.T) {
|
|||
|
||||
func TestGetOAuth2GrantByID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
grant, err := GetOAuth2GrantByID(db.DefaultContext, 1)
|
||||
grant, err := auth_model.GetOAuth2GrantByID(db.DefaultContext, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), grant.ID)
|
||||
|
||||
grant, err = GetOAuth2GrantByID(db.DefaultContext, 34923458)
|
||||
grant, err = auth_model.GetOAuth2GrantByID(db.DefaultContext, 34923458)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, grant)
|
||||
}
|
||||
|
||||
func TestOAuth2Grant_IncreaseCounter(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
grant := unittest.AssertExistsAndLoadBean(t, &OAuth2Grant{ID: 1, Counter: 1})
|
||||
grant := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1, Counter: 1})
|
||||
assert.NoError(t, grant.IncreaseCounter(db.DefaultContext))
|
||||
assert.Equal(t, int64(2), grant.Counter)
|
||||
unittest.AssertExistsAndLoadBean(t, &OAuth2Grant{ID: 1, Counter: 2})
|
||||
unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1, Counter: 2})
|
||||
}
|
||||
|
||||
func TestOAuth2Grant_ScopeContains(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
grant := unittest.AssertExistsAndLoadBean(t, &OAuth2Grant{ID: 1, Scope: "openid profile"})
|
||||
grant := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1, Scope: "openid profile"})
|
||||
assert.True(t, grant.ScopeContains("openid"))
|
||||
assert.True(t, grant.ScopeContains("profile"))
|
||||
assert.False(t, grant.ScopeContains("profil"))
|
||||
|
@ -130,7 +131,7 @@ func TestOAuth2Grant_ScopeContains(t *testing.T) {
|
|||
|
||||
func TestOAuth2Grant_GenerateNewAuthorizationCode(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
grant := unittest.AssertExistsAndLoadBean(t, &OAuth2Grant{ID: 1})
|
||||
grant := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1})
|
||||
code, err := grant.GenerateNewAuthorizationCode(db.DefaultContext, "https://example2.com/callback", "CjvyTLSdR47G5zYenDA-eDWW4lRrO8yvjcWwbD_deOg", "S256")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, code)
|
||||
|
@ -138,46 +139,46 @@ func TestOAuth2Grant_GenerateNewAuthorizationCode(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestOAuth2Grant_TableName(t *testing.T) {
|
||||
assert.Equal(t, "oauth2_grant", new(OAuth2Grant).TableName())
|
||||
assert.Equal(t, "oauth2_grant", new(auth_model.OAuth2Grant).TableName())
|
||||
}
|
||||
|
||||
func TestGetOAuth2GrantsByUserID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
result, err := GetOAuth2GrantsByUserID(db.DefaultContext, 1)
|
||||
result, err := auth_model.GetOAuth2GrantsByUserID(db.DefaultContext, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, int64(1), result[0].ID)
|
||||
assert.Equal(t, result[0].ApplicationID, result[0].Application.ID)
|
||||
|
||||
result, err = GetOAuth2GrantsByUserID(db.DefaultContext, 34134)
|
||||
result, err = auth_model.GetOAuth2GrantsByUserID(db.DefaultContext, 34134)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, result)
|
||||
}
|
||||
|
||||
func TestRevokeOAuth2Grant(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
assert.NoError(t, RevokeOAuth2Grant(db.DefaultContext, 1, 1))
|
||||
unittest.AssertNotExistsBean(t, &OAuth2Grant{ID: 1, UserID: 1})
|
||||
assert.NoError(t, auth_model.RevokeOAuth2Grant(db.DefaultContext, 1, 1))
|
||||
unittest.AssertNotExistsBean(t, &auth_model.OAuth2Grant{ID: 1, UserID: 1})
|
||||
}
|
||||
|
||||
//////////////////// Authorization Code
|
||||
|
||||
func TestGetOAuth2AuthorizationByCode(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
code, err := GetOAuth2AuthorizationByCode(db.DefaultContext, "authcode")
|
||||
code, err := auth_model.GetOAuth2AuthorizationByCode(db.DefaultContext, "authcode")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, code)
|
||||
assert.Equal(t, "authcode", code.Code)
|
||||
assert.Equal(t, int64(1), code.ID)
|
||||
|
||||
code, err = GetOAuth2AuthorizationByCode(db.DefaultContext, "does not exist")
|
||||
code, err = auth_model.GetOAuth2AuthorizationByCode(db.DefaultContext, "does not exist")
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, code)
|
||||
}
|
||||
|
||||
func TestOAuth2AuthorizationCode_ValidateCodeChallenge(t *testing.T) {
|
||||
// test plain
|
||||
code := &OAuth2AuthorizationCode{
|
||||
code := &auth_model.OAuth2AuthorizationCode{
|
||||
CodeChallengeMethod: "plain",
|
||||
CodeChallenge: "test123",
|
||||
}
|
||||
|
@ -185,7 +186,7 @@ func TestOAuth2AuthorizationCode_ValidateCodeChallenge(t *testing.T) {
|
|||
assert.False(t, code.ValidateCodeChallenge("ierwgjoergjio"))
|
||||
|
||||
// test S256
|
||||
code = &OAuth2AuthorizationCode{
|
||||
code = &auth_model.OAuth2AuthorizationCode{
|
||||
CodeChallengeMethod: "S256",
|
||||
CodeChallenge: "CjvyTLSdR47G5zYenDA-eDWW4lRrO8yvjcWwbD_deOg",
|
||||
}
|
||||
|
@ -193,14 +194,14 @@ func TestOAuth2AuthorizationCode_ValidateCodeChallenge(t *testing.T) {
|
|||
assert.False(t, code.ValidateCodeChallenge("wiogjerogorewngoenrgoiuenorg"))
|
||||
|
||||
// test unknown
|
||||
code = &OAuth2AuthorizationCode{
|
||||
code = &auth_model.OAuth2AuthorizationCode{
|
||||
CodeChallengeMethod: "monkey",
|
||||
CodeChallenge: "foiwgjioriogeiogjerger",
|
||||
}
|
||||
assert.False(t, code.ValidateCodeChallenge("foiwgjioriogeiogjerger"))
|
||||
|
||||
// test no code challenge
|
||||
code = &OAuth2AuthorizationCode{
|
||||
code = &auth_model.OAuth2AuthorizationCode{
|
||||
CodeChallengeMethod: "",
|
||||
CodeChallenge: "foierjiogerogerg",
|
||||
}
|
||||
|
@ -208,7 +209,7 @@ func TestOAuth2AuthorizationCode_ValidateCodeChallenge(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestOAuth2AuthorizationCode_GenerateRedirectURI(t *testing.T) {
|
||||
code := &OAuth2AuthorizationCode{
|
||||
code := &auth_model.OAuth2AuthorizationCode{
|
||||
RedirectURI: "https://example.com/callback",
|
||||
Code: "thecode",
|
||||
}
|
||||
|
@ -224,11 +225,11 @@ func TestOAuth2AuthorizationCode_GenerateRedirectURI(t *testing.T) {
|
|||
|
||||
func TestOAuth2AuthorizationCode_Invalidate(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
code := unittest.AssertExistsAndLoadBean(t, &OAuth2AuthorizationCode{Code: "authcode"})
|
||||
code := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2AuthorizationCode{Code: "authcode"})
|
||||
assert.NoError(t, code.Invalidate(db.DefaultContext))
|
||||
unittest.AssertNotExistsBean(t, &OAuth2AuthorizationCode{Code: "authcode"})
|
||||
unittest.AssertNotExistsBean(t, &auth_model.OAuth2AuthorizationCode{Code: "authcode"})
|
||||
}
|
||||
|
||||
func TestOAuth2AuthorizationCode_TableName(t *testing.T) {
|
||||
assert.Equal(t, "oauth2_authorization_code", new(OAuth2AuthorizationCode).TableName())
|
||||
assert.Equal(t, "oauth2_authorization_code", new(auth_model.OAuth2AuthorizationCode).TableName())
|
||||
}
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package auth
|
||||
package auth_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
|
@ -37,13 +38,13 @@ func (source *TestSource) ToDB() ([]byte, error) {
|
|||
func TestDumpAuthSource(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
authSourceSchema, err := db.TableInfo(new(Source))
|
||||
authSourceSchema, err := db.TableInfo(new(auth_model.Source))
|
||||
assert.NoError(t, err)
|
||||
|
||||
RegisterTypeConfig(OAuth2, new(TestSource))
|
||||
auth_model.RegisterTypeConfig(auth_model.OAuth2, new(TestSource))
|
||||
|
||||
CreateSource(&Source{
|
||||
Type: OAuth2,
|
||||
auth_model.CreateSource(&auth_model.Source{
|
||||
Type: auth_model.OAuth2,
|
||||
Name: "TestSource",
|
||||
IsActive: false,
|
||||
Cfg: &TestSource{
|
||||
|
|
|
@ -3,14 +3,13 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
@ -21,6 +20,34 @@ import (
|
|||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
// ErrAccessTokenNotExist represents a "AccessTokenNotExist" kind of error.
|
||||
type ErrAccessTokenNotExist struct {
|
||||
Token string
|
||||
}
|
||||
|
||||
// IsErrAccessTokenNotExist checks if an error is a ErrAccessTokenNotExist.
|
||||
func IsErrAccessTokenNotExist(err error) bool {
|
||||
_, ok := err.(ErrAccessTokenNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrAccessTokenNotExist) Error() string {
|
||||
return fmt.Sprintf("access token does not exist [sha: %s]", err.Token)
|
||||
}
|
||||
|
||||
// ErrAccessTokenEmpty represents a "AccessTokenEmpty" kind of error.
|
||||
type ErrAccessTokenEmpty struct{}
|
||||
|
||||
// IsErrAccessTokenEmpty checks if an error is a ErrAccessTokenEmpty.
|
||||
func IsErrAccessTokenEmpty(err error) bool {
|
||||
_, ok := err.(ErrAccessTokenEmpty)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrAccessTokenEmpty) Error() string {
|
||||
return "access token is empty"
|
||||
}
|
||||
|
||||
var successfulAccessTokenCache *lru.Cache
|
||||
|
||||
// AccessToken represents a personal access token.
|
||||
|
@ -68,7 +95,7 @@ func NewAccessToken(t *AccessToken) error {
|
|||
}
|
||||
t.TokenSalt = salt
|
||||
t.Token = base.EncodeSha1(gouuid.New().String())
|
||||
t.TokenHash = auth.HashToken(t.Token, t.TokenSalt)
|
||||
t.TokenHash = HashToken(t.Token, t.TokenSalt)
|
||||
t.TokenLastEight = t.Token[len(t.Token)-8:]
|
||||
_, err = db.GetEngine(db.DefaultContext).Insert(t)
|
||||
return err
|
||||
|
@ -130,7 +157,7 @@ func GetAccessTokenBySHA(token string) (*AccessToken, error) {
|
|||
}
|
||||
|
||||
for _, t := range tokens {
|
||||
tempHash := auth.HashToken(token, t.TokenSalt)
|
||||
tempHash := HashToken(token, t.TokenSalt)
|
||||
if subtle.ConstantTimeCompare([]byte(t.TokenHash), []byte(tempHash)) == 1 {
|
||||
if successfulAccessTokenCache != nil {
|
||||
successfulAccessTokenCache.Add(token, t.ID)
|
|
@ -2,11 +2,12 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
package auth_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -14,77 +15,77 @@ import (
|
|||
|
||||
func TestNewAccessToken(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
token := &AccessToken{
|
||||
token := &auth_model.AccessToken{
|
||||
UID: 3,
|
||||
Name: "Token C",
|
||||
}
|
||||
assert.NoError(t, NewAccessToken(token))
|
||||
assert.NoError(t, auth_model.NewAccessToken(token))
|
||||
unittest.AssertExistsAndLoadBean(t, token)
|
||||
|
||||
invalidToken := &AccessToken{
|
||||
invalidToken := &auth_model.AccessToken{
|
||||
ID: token.ID, // duplicate
|
||||
UID: 2,
|
||||
Name: "Token F",
|
||||
}
|
||||
assert.Error(t, NewAccessToken(invalidToken))
|
||||
assert.Error(t, auth_model.NewAccessToken(invalidToken))
|
||||
}
|
||||
|
||||
func TestAccessTokenByNameExists(t *testing.T) {
|
||||
name := "Token Gitea"
|
||||
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
token := &AccessToken{
|
||||
token := &auth_model.AccessToken{
|
||||
UID: 3,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
// Check to make sure it doesn't exists already
|
||||
exist, err := AccessTokenByNameExists(token)
|
||||
exist, err := auth_model.AccessTokenByNameExists(token)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, exist)
|
||||
|
||||
// Save it to the database
|
||||
assert.NoError(t, NewAccessToken(token))
|
||||
assert.NoError(t, auth_model.NewAccessToken(token))
|
||||
unittest.AssertExistsAndLoadBean(t, token)
|
||||
|
||||
// This token must be found by name in the DB now
|
||||
exist, err = AccessTokenByNameExists(token)
|
||||
exist, err = auth_model.AccessTokenByNameExists(token)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, exist)
|
||||
|
||||
user4Token := &AccessToken{
|
||||
user4Token := &auth_model.AccessToken{
|
||||
UID: 4,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
// Name matches but different user ID, this shouldn't exists in the
|
||||
// database
|
||||
exist, err = AccessTokenByNameExists(user4Token)
|
||||
exist, err = auth_model.AccessTokenByNameExists(user4Token)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, exist)
|
||||
}
|
||||
|
||||
func TestGetAccessTokenBySHA(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
token, err := GetAccessTokenBySHA("d2c6c1ba3890b309189a8e618c72a162e4efbf36")
|
||||
token, err := auth_model.GetAccessTokenBySHA("d2c6c1ba3890b309189a8e618c72a162e4efbf36")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), token.UID)
|
||||
assert.Equal(t, "Token A", token.Name)
|
||||
assert.Equal(t, "2b3668e11cb82d3af8c6e4524fc7841297668f5008d1626f0ad3417e9fa39af84c268248b78c481daa7e5dc437784003494f", token.TokenHash)
|
||||
assert.Equal(t, "e4efbf36", token.TokenLastEight)
|
||||
|
||||
_, err = GetAccessTokenBySHA("notahash")
|
||||
_, err = auth_model.GetAccessTokenBySHA("notahash")
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrAccessTokenNotExist(err))
|
||||
assert.True(t, auth_model.IsErrAccessTokenNotExist(err))
|
||||
|
||||
_, err = GetAccessTokenBySHA("")
|
||||
_, err = auth_model.GetAccessTokenBySHA("")
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrAccessTokenEmpty(err))
|
||||
assert.True(t, auth_model.IsErrAccessTokenEmpty(err))
|
||||
}
|
||||
|
||||
func TestListAccessTokens(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
tokens, err := ListAccessTokens(ListAccessTokensOptions{UserID: 1})
|
||||
tokens, err := auth_model.ListAccessTokens(auth_model.ListAccessTokensOptions{UserID: 1})
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, tokens, 2) {
|
||||
assert.Equal(t, int64(1), tokens[0].UID)
|
||||
|
@ -93,39 +94,39 @@ func TestListAccessTokens(t *testing.T) {
|
|||
assert.Contains(t, []string{tokens[0].Name, tokens[1].Name}, "Token B")
|
||||
}
|
||||
|
||||
tokens, err = ListAccessTokens(ListAccessTokensOptions{UserID: 2})
|
||||
tokens, err = auth_model.ListAccessTokens(auth_model.ListAccessTokensOptions{UserID: 2})
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, tokens, 1) {
|
||||
assert.Equal(t, int64(2), tokens[0].UID)
|
||||
assert.Equal(t, "Token A", tokens[0].Name)
|
||||
}
|
||||
|
||||
tokens, err = ListAccessTokens(ListAccessTokensOptions{UserID: 100})
|
||||
tokens, err = auth_model.ListAccessTokens(auth_model.ListAccessTokensOptions{UserID: 100})
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, tokens)
|
||||
}
|
||||
|
||||
func TestUpdateAccessToken(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
token, err := GetAccessTokenBySHA("4c6f36e6cf498e2a448662f915d932c09c5a146c")
|
||||
token, err := auth_model.GetAccessTokenBySHA("4c6f36e6cf498e2a448662f915d932c09c5a146c")
|
||||
assert.NoError(t, err)
|
||||
token.Name = "Token Z"
|
||||
|
||||
assert.NoError(t, UpdateAccessToken(token))
|
||||
assert.NoError(t, auth_model.UpdateAccessToken(token))
|
||||
unittest.AssertExistsAndLoadBean(t, token)
|
||||
}
|
||||
|
||||
func TestDeleteAccessTokenByID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
token, err := GetAccessTokenBySHA("4c6f36e6cf498e2a448662f915d932c09c5a146c")
|
||||
token, err := auth_model.GetAccessTokenBySHA("4c6f36e6cf498e2a448662f915d932c09c5a146c")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), token.UID)
|
||||
|
||||
assert.NoError(t, DeleteAccessTokenByID(token.ID, 1))
|
||||
assert.NoError(t, auth_model.DeleteAccessTokenByID(token.ID, 1))
|
||||
unittest.AssertNotExistsBean(t, token)
|
||||
|
||||
err = DeleteAccessTokenByID(100, 100)
|
||||
err = auth_model.DeleteAccessTokenByID(100, 100)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrAccessTokenNotExist(err))
|
||||
assert.True(t, auth_model.IsErrAccessTokenNotExist(err))
|
||||
}
|
|
@ -2,11 +2,12 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package auth
|
||||
package auth_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
"github.com/duo-labs/webauthn/webauthn"
|
||||
|
@ -16,51 +17,51 @@ import (
|
|||
func TestGetWebAuthnCredentialByID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
res, err := GetWebAuthnCredentialByID(1)
|
||||
res, err := auth_model.GetWebAuthnCredentialByID(1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "WebAuthn credential", res.Name)
|
||||
|
||||
_, err = GetWebAuthnCredentialByID(342432)
|
||||
_, err = auth_model.GetWebAuthnCredentialByID(342432)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrWebAuthnCredentialNotExist(err))
|
||||
assert.True(t, auth_model.IsErrWebAuthnCredentialNotExist(err))
|
||||
}
|
||||
|
||||
func TestGetWebAuthnCredentialsByUID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
res, err := GetWebAuthnCredentialsByUID(32)
|
||||
res, err := auth_model.GetWebAuthnCredentialsByUID(32)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, res, 1)
|
||||
assert.Equal(t, "WebAuthn credential", res[0].Name)
|
||||
}
|
||||
|
||||
func TestWebAuthnCredential_TableName(t *testing.T) {
|
||||
assert.Equal(t, "webauthn_credential", WebAuthnCredential{}.TableName())
|
||||
assert.Equal(t, "webauthn_credential", auth_model.WebAuthnCredential{}.TableName())
|
||||
}
|
||||
|
||||
func TestWebAuthnCredential_UpdateSignCount(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
cred := unittest.AssertExistsAndLoadBean(t, &WebAuthnCredential{ID: 1})
|
||||
cred := unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1})
|
||||
cred.SignCount = 1
|
||||
assert.NoError(t, cred.UpdateSignCount())
|
||||
unittest.AssertExistsIf(t, true, &WebAuthnCredential{ID: 1, SignCount: 1})
|
||||
unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{ID: 1, SignCount: 1})
|
||||
}
|
||||
|
||||
func TestWebAuthnCredential_UpdateLargeCounter(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
cred := unittest.AssertExistsAndLoadBean(t, &WebAuthnCredential{ID: 1})
|
||||
cred := unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1})
|
||||
cred.SignCount = 0xffffffff
|
||||
assert.NoError(t, cred.UpdateSignCount())
|
||||
unittest.AssertExistsIf(t, true, &WebAuthnCredential{ID: 1, SignCount: 0xffffffff})
|
||||
unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{ID: 1, SignCount: 0xffffffff})
|
||||
}
|
||||
|
||||
func TestCreateCredential(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
res, err := CreateCredential(1, "WebAuthn Created Credential", &webauthn.Credential{ID: []byte("Test")})
|
||||
res, err := auth_model.CreateCredential(1, "WebAuthn Created Credential", &webauthn.Credential{ID: []byte("Test")})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "WebAuthn Created Credential", res.Name)
|
||||
assert.Equal(t, []byte("Test"), res.CredentialID)
|
||||
|
||||
unittest.AssertExistsIf(t, true, &WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1})
|
||||
unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1})
|
||||
}
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
// Copyright 2017 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 models
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// CountNullArchivedRepository counts the number of repositories with is_archived is null
|
||||
func CountNullArchivedRepository() (int64, error) {
|
||||
return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Count(new(repo_model.Repository))
|
||||
}
|
||||
|
||||
// FixNullArchivedRepository sets is_archived to false where it is null
|
||||
func FixNullArchivedRepository() (int64, error) {
|
||||
return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Cols("is_archived").NoAutoTime().Update(&repo_model.Repository{
|
||||
IsArchived: false,
|
||||
})
|
||||
}
|
||||
|
||||
// CountWrongUserType count OrgUser who have wrong type
|
||||
func CountWrongUserType() (int64, error) {
|
||||
return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Count(new(user_model.User))
|
||||
}
|
||||
|
||||
// FixWrongUserType fix OrgUser who have wrong type
|
||||
func FixWrongUserType() (int64, error) {
|
||||
return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Cols("type").NoAutoTime().Update(&user_model.User{Type: 1})
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
|
@ -5,7 +5,6 @@
|
|||
package db_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
|
@ -20,8 +19,7 @@ import (
|
|||
func TestDumpDatabase(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
dir, err := os.MkdirTemp(os.TempDir(), "dump")
|
||||
assert.NoError(t, err)
|
||||
dir := t.TempDir()
|
||||
|
||||
type Version struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
|
|
203
models/error.go
203
models/error.go
|
@ -57,93 +57,6 @@ func (err ErrUserOwnPackages) Error() string {
|
|||
return fmt.Sprintf("user still has ownership of packages [uid: %d]", err.UID)
|
||||
}
|
||||
|
||||
// __ __.__ __ .__
|
||||
// / \ / \__| | _|__|
|
||||
// \ \/\/ / | |/ / |
|
||||
// \ /| | <| |
|
||||
// \__/\ / |__|__|_ \__|
|
||||
// \/ \/
|
||||
|
||||
// ErrWikiAlreadyExist represents a "WikiAlreadyExist" kind of error.
|
||||
type ErrWikiAlreadyExist struct {
|
||||
Title string
|
||||
}
|
||||
|
||||
// IsErrWikiAlreadyExist checks if an error is an ErrWikiAlreadyExist.
|
||||
func IsErrWikiAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrWikiAlreadyExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrWikiAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("wiki page already exists [title: %s]", err.Title)
|
||||
}
|
||||
|
||||
// ErrWikiReservedName represents a reserved name error.
|
||||
type ErrWikiReservedName struct {
|
||||
Title string
|
||||
}
|
||||
|
||||
// IsErrWikiReservedName checks if an error is an ErrWikiReservedName.
|
||||
func IsErrWikiReservedName(err error) bool {
|
||||
_, ok := err.(ErrWikiReservedName)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrWikiReservedName) Error() string {
|
||||
return fmt.Sprintf("wiki title is reserved: %s", err.Title)
|
||||
}
|
||||
|
||||
// ErrWikiInvalidFileName represents an invalid wiki file name.
|
||||
type ErrWikiInvalidFileName struct {
|
||||
FileName string
|
||||
}
|
||||
|
||||
// IsErrWikiInvalidFileName checks if an error is an ErrWikiInvalidFileName.
|
||||
func IsErrWikiInvalidFileName(err error) bool {
|
||||
_, ok := err.(ErrWikiInvalidFileName)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrWikiInvalidFileName) Error() string {
|
||||
return fmt.Sprintf("Invalid wiki filename: %s", err.FileName)
|
||||
}
|
||||
|
||||
// _____ ___________ __
|
||||
// / _ \ ____ ____ ____ ______ _____\__ ___/___ | | __ ____ ____
|
||||
// / /_\ \_/ ___\/ ___\/ __ \ / ___// ___/ | | / _ \| |/ // __ \ / \
|
||||
// / | \ \__\ \__\ ___/ \___ \ \___ \ | |( <_> ) <\ ___/| | \
|
||||
// \____|__ /\___ >___ >___ >____ >____ > |____| \____/|__|_ \\___ >___| /
|
||||
// \/ \/ \/ \/ \/ \/ \/ \/ \/
|
||||
|
||||
// ErrAccessTokenNotExist represents a "AccessTokenNotExist" kind of error.
|
||||
type ErrAccessTokenNotExist struct {
|
||||
Token string
|
||||
}
|
||||
|
||||
// IsErrAccessTokenNotExist checks if an error is a ErrAccessTokenNotExist.
|
||||
func IsErrAccessTokenNotExist(err error) bool {
|
||||
_, ok := err.(ErrAccessTokenNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrAccessTokenNotExist) Error() string {
|
||||
return fmt.Sprintf("access token does not exist [sha: %s]", err.Token)
|
||||
}
|
||||
|
||||
// ErrAccessTokenEmpty represents a "AccessTokenEmpty" kind of error.
|
||||
type ErrAccessTokenEmpty struct{}
|
||||
|
||||
// IsErrAccessTokenEmpty checks if an error is a ErrAccessTokenEmpty.
|
||||
func IsErrAccessTokenEmpty(err error) bool {
|
||||
_, ok := err.(ErrAccessTokenEmpty)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrAccessTokenEmpty) Error() string {
|
||||
return "access token is empty"
|
||||
}
|
||||
|
||||
// ErrNoPendingRepoTransfer is an error type for repositories without a pending
|
||||
// transfer request
|
||||
type ErrNoPendingRepoTransfer struct {
|
||||
|
@ -178,23 +91,6 @@ func (err ErrRepoTransferInProgress) Error() string {
|
|||
return fmt.Sprintf("repository is already being transferred [uname: %s, name: %s]", err.Uname, err.Name)
|
||||
}
|
||||
|
||||
// ErrForkAlreadyExist represents a "ForkAlreadyExist" kind of error.
|
||||
type ErrForkAlreadyExist struct {
|
||||
Uname string
|
||||
RepoName string
|
||||
ForkName string
|
||||
}
|
||||
|
||||
// IsErrForkAlreadyExist checks if an error is an ErrForkAlreadyExist.
|
||||
func IsErrForkAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrForkAlreadyExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrForkAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("repository is already forked by user [uname: %s, repo path: %s, fork path: %s]", err.Uname, err.RepoName, err.ForkName)
|
||||
}
|
||||
|
||||
// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
|
||||
type ErrInvalidCloneAddr struct {
|
||||
Host string
|
||||
|
@ -243,37 +139,6 @@ func (err ErrUpdateTaskNotExist) Error() string {
|
|||
return fmt.Sprintf("update task does not exist [uuid: %s]", err.UUID)
|
||||
}
|
||||
|
||||
// ErrReleaseAlreadyExist represents a "ReleaseAlreadyExist" kind of error.
|
||||
type ErrReleaseAlreadyExist struct {
|
||||
TagName string
|
||||
}
|
||||
|
||||
// IsErrReleaseAlreadyExist checks if an error is a ErrReleaseAlreadyExist.
|
||||
func IsErrReleaseAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrReleaseAlreadyExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrReleaseAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("release tag already exist [tag_name: %s]", err.TagName)
|
||||
}
|
||||
|
||||
// ErrReleaseNotExist represents a "ReleaseNotExist" kind of error.
|
||||
type ErrReleaseNotExist struct {
|
||||
ID int64
|
||||
TagName string
|
||||
}
|
||||
|
||||
// IsErrReleaseNotExist checks if an error is a ErrReleaseNotExist.
|
||||
func IsErrReleaseNotExist(err error) bool {
|
||||
_, ok := err.(ErrReleaseNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrReleaseNotExist) Error() string {
|
||||
return fmt.Sprintf("release tag does not exist [id: %d, tag_name: %s]", err.ID, err.TagName)
|
||||
}
|
||||
|
||||
// ErrInvalidTagName represents a "InvalidTagName" kind of error.
|
||||
type ErrInvalidTagName struct {
|
||||
TagName string
|
||||
|
@ -657,71 +522,3 @@ func (err ErrPullRequestHasMerged) Error() string {
|
|||
return fmt.Sprintf("pull request has merged [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]",
|
||||
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
|
||||
}
|
||||
|
||||
// _________ __ __ .__
|
||||
// / _____// |_ ____ ________ _ _______ _/ |_ ____ | |__
|
||||
// \_____ \\ __\/ _ \\____ \ \/ \/ /\__ \\ __\/ ___\| | \
|
||||
// / \| | ( <_> ) |_> > / / __ \| | \ \___| Y \
|
||||
// /_______ /|__| \____/| __/ \/\_/ (____ /__| \___ >___| /
|
||||
// \/ |__| \/ \/ \/
|
||||
|
||||
// ErrStopwatchNotExist represents a "Stopwatch Not Exist" kind of error.
|
||||
type ErrStopwatchNotExist struct {
|
||||
ID int64
|
||||
}
|
||||
|
||||
// IsErrStopwatchNotExist checks if an error is a ErrStopwatchNotExist.
|
||||
func IsErrStopwatchNotExist(err error) bool {
|
||||
_, ok := err.(ErrStopwatchNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrStopwatchNotExist) Error() string {
|
||||
return fmt.Sprintf("stopwatch does not exist [id: %d]", err.ID)
|
||||
}
|
||||
|
||||
// ___________ __ .______________.__
|
||||
// \__ ___/___________ ____ | | __ ____ __| _/\__ ___/|__| _____ ____
|
||||
// | | \_ __ \__ \ _/ ___\| |/ // __ \ / __ | | | | |/ \_/ __ \
|
||||
// | | | | \// __ \\ \___| <\ ___// /_/ | | | | | Y Y \ ___/
|
||||
// |____| |__| (____ /\___ >__|_ \\___ >____ | |____| |__|__|_| /\___ >
|
||||
// \/ \/ \/ \/ \/ \/ \/
|
||||
|
||||
// ErrTrackedTimeNotExist represents a "TrackedTime Not Exist" kind of error.
|
||||
type ErrTrackedTimeNotExist struct {
|
||||
ID int64
|
||||
}
|
||||
|
||||
// IsErrTrackedTimeNotExist checks if an error is a ErrTrackedTimeNotExist.
|
||||
func IsErrTrackedTimeNotExist(err error) bool {
|
||||
_, ok := err.(ErrTrackedTimeNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrTrackedTimeNotExist) Error() string {
|
||||
return fmt.Sprintf("tracked time does not exist [id: %d]", err.ID)
|
||||
}
|
||||
|
||||
// ____ ___ .__ .___
|
||||
// | | \______ | | _________ __| _/
|
||||
// | | /\____ \| | / _ \__ \ / __ |
|
||||
// | | / | |_> > |_( <_> ) __ \_/ /_/ |
|
||||
// |______/ | __/|____/\____(____ /\____ |
|
||||
// |__| \/ \/
|
||||
//
|
||||
|
||||
// ErrUploadNotExist represents a "UploadNotExist" kind of error.
|
||||
type ErrUploadNotExist struct {
|
||||
ID int64
|
||||
UUID string
|
||||
}
|
||||
|
||||
// IsErrUploadNotExist checks if an error is a ErrUploadNotExist.
|
||||
func IsErrUploadNotExist(err error) bool {
|
||||
_, ok := err.(ErrUploadNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUploadNotExist) Error() string {
|
||||
return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID)
|
||||
}
|
||||
|
|
|
@ -63,3 +63,9 @@
|
|||
uid: 29
|
||||
org_id: 17
|
||||
is_public: true
|
||||
|
||||
-
|
||||
id: 12
|
||||
uid: 2
|
||||
org_id: 17
|
||||
is_public: true
|
||||
|
|
|
@ -309,7 +309,7 @@
|
|||
avatar_email: user17@example.com
|
||||
num_repos: 2
|
||||
is_active: true
|
||||
num_members: 3
|
||||
num_members: 4
|
||||
num_teams: 3
|
||||
|
||||
-
|
||||
|
|
|
@ -68,6 +68,7 @@ func LoadIssuesFromBoard(b *project_model.Board) (IssueList, error) {
|
|||
issues, err := Issues(&IssuesOptions{
|
||||
ProjectBoardID: b.ID,
|
||||
ProjectID: b.ProjectID,
|
||||
SortType: "project-column-sorting",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -79,6 +80,7 @@ func LoadIssuesFromBoard(b *project_model.Board) (IssueList, error) {
|
|||
issues, err := Issues(&IssuesOptions{
|
||||
ProjectBoardID: -1, // Issues without ProjectBoardID
|
||||
ProjectID: b.ProjectID,
|
||||
SortType: "project-column-sorting",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -7,6 +7,7 @@ package models
|
|||
import (
|
||||
"testing"
|
||||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
@ -28,7 +29,7 @@ func TestFixturesAreConsistent(t *testing.T) {
|
|||
&user_model.User{},
|
||||
&repo_model.Repository{},
|
||||
&organization.Team{},
|
||||
&Action{})
|
||||
&activities_model.Action{})
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
|
@ -154,7 +155,7 @@ func InsertPullRequests(prs ...*issues_model.PullRequest) error {
|
|||
}
|
||||
|
||||
// InsertReleases migrates release
|
||||
func InsertReleases(rels ...*Release) error {
|
||||
func InsertReleases(rels ...*repo_model.Release) error {
|
||||
ctx, committer, err := db.TxContext()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -191,7 +192,7 @@ func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID string, us
|
|||
return err
|
||||
}
|
||||
|
||||
if err := UpdateReleasesMigrationsByType(tp, externalUserID, userID); err != nil {
|
||||
if err := repo_model.UpdateReleasesMigrationsByType(tp, externalUserID, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -149,7 +149,7 @@ func TestMigrate_InsertReleases(t *testing.T) {
|
|||
a := &repo_model.Attachment{
|
||||
UUID: "a0eebc91-9c0c-4ef7-bb6e-6bb9bd380a12",
|
||||
}
|
||||
r := &Release{
|
||||
r := &repo_model.Release{
|
||||
Attachments: []*repo_model.Attachment{a},
|
||||
}
|
||||
|
||||
|
|
|
@ -406,8 +406,13 @@ var migrations = []Migration{
|
|||
NewMigration("Drop old CredentialID column", dropOldCredentialIDColumn),
|
||||
// v223 -> v224
|
||||
NewMigration("Rename CredentialIDBytes column to CredentialID", renameCredentialIDBytes),
|
||||
|
||||
// Gitea 1.17.0 ends at v224
|
||||
|
||||
// v224 -> v225
|
||||
NewMigration("Add badges to users", creatUserBadgesTable),
|
||||
NewMigration("Add badges to users", createUserBadgesTable),
|
||||
// v225 -> v226
|
||||
NewMigration("Alter gpg_key/public_key content TEXT fields to MEDIUMTEXT", alterPublicGPGKeyContentFieldsToMediumText),
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current db version
|
||||
|
|
|
@ -46,7 +46,7 @@ func TestMain(m *testing.M) {
|
|||
|
||||
giteaConf := os.Getenv("GITEA_CONF")
|
||||
if giteaConf == "" {
|
||||
giteaConf = path.Join(filepath.Dir(setting.AppPath), "integrations/sqlite.ini")
|
||||
giteaConf = path.Join(filepath.Dir(setting.AppPath), "tests/sqlite.ini")
|
||||
fmt.Printf("Environment variable $GITEA_CONF not set - defaulting to %s\n", giteaConf)
|
||||
}
|
||||
|
||||
|
@ -205,7 +205,7 @@ func prepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.En
|
|||
ourSkip += skip
|
||||
deferFn := PrintCurrentTest(t, ourSkip)
|
||||
assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
|
||||
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
|
||||
assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath))
|
||||
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
||||
if err != nil {
|
||||
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
|
||||
|
|
|
@ -188,5 +188,5 @@ func (log *TestLogger) GetName() string {
|
|||
func init() {
|
||||
log.Register("test", NewTestLogger)
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
prefix = strings.TrimSuffix(filename, "integrations/testlogger.go")
|
||||
prefix = strings.TrimSuffix(filename, "tests/testlogger.go")
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package migrations
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"xorm.io/xorm"
|
||||
|
@ -37,8 +38,14 @@ func (*improveActionTableIndicesAction) TableIndices() []*schemas.Index {
|
|||
|
||||
actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
|
||||
actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")
|
||||
indices := []*schemas.Index{actUserIndex, repoIndex}
|
||||
if setting.Database.UsePostgreSQL {
|
||||
cudIndex := schemas.NewIndex("c_u_d", schemas.IndexType)
|
||||
cudIndex.AddColumn("created_unix", "user_id", "is_deleted")
|
||||
indices = append(indices, cudIndex)
|
||||
}
|
||||
|
||||
return []*schemas.Index{actUserIndex, repoIndex}
|
||||
return indices
|
||||
}
|
||||
|
||||
func improveActionTableIndices(x *xorm.Engine) error {
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func creatUserBadgesTable(x *xorm.Engine) error {
|
||||
func createUserBadgesTable(x *xorm.Engine) error {
|
||||
type Badge struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Description string
|
||||
|
|
29
models/migrations/v225.go
Normal file
29
models/migrations/v225.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
// 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 migrations
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func alterPublicGPGKeyContentFieldsToMediumText(x *xorm.Engine) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if setting.Database.UseMySQL {
|
||||
if _, err := sess.Exec("ALTER TABLE `gpg_key` CHANGE `content` `content` MEDIUMTEXT"); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := sess.Exec("ALTER TABLE `public_key` CHANGE `content` `content` MEDIUMTEXT"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
|
@ -8,79 +8,13 @@ package models
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// MinimalOrg represents a simple orgnization with only needed columns
|
||||
type MinimalOrg = organization.Organization
|
||||
|
||||
// GetUserOrgsList returns one user's all orgs list
|
||||
func GetUserOrgsList(user *user_model.User) ([]*MinimalOrg, error) {
|
||||
schema, err := db.TableInfo(new(user_model.User))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputCols := []string{
|
||||
"id",
|
||||
"name",
|
||||
"full_name",
|
||||
"visibility",
|
||||
"avatar",
|
||||
"avatar_email",
|
||||
"use_custom_avatar",
|
||||
}
|
||||
|
||||
groupByCols := &strings.Builder{}
|
||||
for _, col := range outputCols {
|
||||
fmt.Fprintf(groupByCols, "`%s`.%s,", schema.Name, col)
|
||||
}
|
||||
groupByStr := groupByCols.String()
|
||||
groupByStr = groupByStr[0 : len(groupByStr)-1]
|
||||
|
||||
sess := db.GetEngine(db.DefaultContext)
|
||||
sess = sess.Select(groupByStr+", count(distinct repo_id) as org_count").
|
||||
Table("user").
|
||||
Join("INNER", "team", "`team`.org_id = `user`.id").
|
||||
Join("INNER", "team_user", "`team`.id = `team_user`.team_id").
|
||||
Join("LEFT", builder.
|
||||
Select("id as repo_id, owner_id as repo_owner_id").
|
||||
From("repository").
|
||||
Where(repo_model.AccessibleRepositoryCondition(user, unit.TypeInvalid)), "`repository`.repo_owner_id = `team`.org_id").
|
||||
Where("`team_user`.uid = ?", user.ID).
|
||||
GroupBy(groupByStr)
|
||||
|
||||
type OrgCount struct {
|
||||
organization.Organization `xorm:"extends"`
|
||||
OrgCount int
|
||||
}
|
||||
|
||||
orgCounts := make([]*OrgCount, 0, 10)
|
||||
|
||||
if err := sess.
|
||||
Asc("`user`.name").
|
||||
Find(&orgCounts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
orgs := make([]*MinimalOrg, len(orgCounts))
|
||||
for i, orgCount := range orgCounts {
|
||||
orgCount.Organization.NumRepos = orgCount.OrgCount
|
||||
orgs[i] = &orgCount.Organization
|
||||
}
|
||||
|
||||
return orgs, nil
|
||||
}
|
||||
|
||||
func removeOrgUser(ctx context.Context, orgID, userID int64) error {
|
||||
ou := new(organization.OrgUser)
|
||||
|
||||
|
|
|
@ -25,12 +25,12 @@ import (
|
|||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
func addRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) {
|
||||
func AddRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) {
|
||||
if err = organization.AddTeamRepo(ctx, t.OrgID, t.ID, repo.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = db.GetEngine(ctx).Incr("num_repos").ID(t.ID).Update(new(organization.Team)); err != nil {
|
||||
if err = organization.IncrTeamRepoNum(ctx, t.ID); err != nil {
|
||||
return fmt.Errorf("update team: %v", err)
|
||||
}
|
||||
|
||||
|
@ -58,16 +58,15 @@ func addRepository(ctx context.Context, t *organization.Team, repo *repo_model.R
|
|||
// addAllRepositories adds all repositories to the team.
|
||||
// If the team already has some repositories they will be left unchanged.
|
||||
func addAllRepositories(ctx context.Context, t *organization.Team) error {
|
||||
var orgRepos []repo_model.Repository
|
||||
e := db.GetEngine(ctx)
|
||||
if err := e.Where("owner_id = ?", t.OrgID).Find(&orgRepos); err != nil {
|
||||
orgRepos, err := organization.GetOrgRepositories(ctx, t.OrgID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get org repos: %v", err)
|
||||
}
|
||||
|
||||
for _, repo := range orgRepos {
|
||||
if !organization.HasTeamRepo(ctx, t.OrgID, t.ID, repo.ID) {
|
||||
if err := addRepository(ctx, t, &repo); err != nil {
|
||||
return fmt.Errorf("addRepository: %v", err)
|
||||
if err := AddRepository(ctx, t, repo); err != nil {
|
||||
return fmt.Errorf("AddRepository: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,27 +89,6 @@ func AddAllRepositories(t *organization.Team) (err error) {
|
|||
return committer.Commit()
|
||||
}
|
||||
|
||||
// AddRepository adds new repository to team of organization.
|
||||
func AddRepository(t *organization.Team, repo *repo_model.Repository) (err error) {
|
||||
if repo.OwnerID != t.OrgID {
|
||||
return errors.New("Repository does not belong to organization")
|
||||
} else if HasRepository(t, repo.ID) {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx, committer, err := db.TxContext()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer committer.Close()
|
||||
|
||||
if err = addRepository(ctx, t, repo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return committer.Commit()
|
||||
}
|
||||
|
||||
// RemoveAllRepositories removes all repositories from team and recalculates access
|
||||
func RemoveAllRepositories(t *organization.Team) (err error) {
|
||||
if t.IncludesAllRepositories {
|
||||
|
|
|
@ -68,25 +68,6 @@ func TestTeam_HasRepository(t *testing.T) {
|
|||
test(2, 5, false)
|
||||
}
|
||||
|
||||
func TestTeam_AddRepository(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
testSuccess := func(teamID, repoID int64) {
|
||||
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
|
||||
assert.NoError(t, AddRepository(team, repo))
|
||||
unittest.AssertExistsAndLoadBean(t, &organization.TeamRepo{TeamID: teamID, RepoID: repoID})
|
||||
unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID}, &repo_model.Repository{ID: repoID})
|
||||
}
|
||||
testSuccess(2, 3)
|
||||
testSuccess(2, 5)
|
||||
|
||||
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1})
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
assert.Error(t, AddRepository(team, repo))
|
||||
unittest.CheckConsistencyFor(t, &organization.Team{ID: 1}, &repo_model.Repository{ID: 1})
|
||||
}
|
||||
|
||||
func TestTeam_RemoveRepository(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
|
|
78
models/organization/mini_org.go
Normal file
78
models/organization/mini_org.go
Normal file
|
@ -0,0 +1,78 @@
|
|||
// 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 organization
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// MinimalOrg represents a simple organization with only the needed columns
|
||||
type MinimalOrg = Organization
|
||||
|
||||
// GetUserOrgsList returns all organizations the given user has access to
|
||||
func GetUserOrgsList(user *user_model.User) ([]*MinimalOrg, error) {
|
||||
schema, err := db.TableInfo(new(user_model.User))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
outputCols := []string{
|
||||
"id",
|
||||
"name",
|
||||
"full_name",
|
||||
"visibility",
|
||||
"avatar",
|
||||
"avatar_email",
|
||||
"use_custom_avatar",
|
||||
}
|
||||
|
||||
groupByCols := &strings.Builder{}
|
||||
for _, col := range outputCols {
|
||||
fmt.Fprintf(groupByCols, "`%s`.%s,", schema.Name, col)
|
||||
}
|
||||
groupByStr := groupByCols.String()
|
||||
groupByStr = groupByStr[0 : len(groupByStr)-1]
|
||||
|
||||
sess := db.GetEngine(db.DefaultContext)
|
||||
sess = sess.Select(groupByStr+", count(distinct repo_id) as org_count").
|
||||
Table("user").
|
||||
Join("INNER", "team", "`team`.org_id = `user`.id").
|
||||
Join("INNER", "team_user", "`team`.id = `team_user`.team_id").
|
||||
Join("LEFT", builder.
|
||||
Select("id as repo_id, owner_id as repo_owner_id").
|
||||
From("repository").
|
||||
Where(repo_model.AccessibleRepositoryCondition(user, unit.TypeInvalid)), "`repository`.repo_owner_id = `team`.org_id").
|
||||
Where("`team_user`.uid = ?", user.ID).
|
||||
GroupBy(groupByStr)
|
||||
|
||||
type OrgCount struct {
|
||||
Organization `xorm:"extends"`
|
||||
OrgCount int
|
||||
}
|
||||
|
||||
orgCounts := make([]*OrgCount, 0, 10)
|
||||
|
||||
if err := sess.
|
||||
Asc("`user`.name").
|
||||
Find(&orgCounts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
orgs := make([]*MinimalOrg, len(orgCounts))
|
||||
for i, orgCount := range orgCounts {
|
||||
orgCount.Organization.NumRepos = orgCount.OrgCount
|
||||
orgs[i] = &orgCount.Organization
|
||||
}
|
||||
|
||||
return orgs, nil
|
||||
}
|
18
models/organization/org_repo.go
Normal file
18
models/organization/org_repo.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 organization
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
)
|
||||
|
||||
// GetOrgRepositories get repos belonging to the given organization
|
||||
func GetOrgRepositories(ctx context.Context, orgID int64) ([]*repo_model.Repository, error) {
|
||||
var orgRepos []*repo_model.Repository
|
||||
return orgRepos, db.GetEngine(ctx).Where("owner_id = ?", orgID).Find(&orgRepos)
|
||||
}
|
|
@ -96,16 +96,7 @@ type SearchTeamOptions struct {
|
|||
IncludeDesc bool
|
||||
}
|
||||
|
||||
// SearchTeam search for teams. Caller is responsible to check permissions.
|
||||
func SearchTeam(opts *SearchTeamOptions) ([]*Team, int64, error) {
|
||||
if opts.Page <= 0 {
|
||||
opts.Page = 1
|
||||
}
|
||||
if opts.PageSize == 0 {
|
||||
// Default limit
|
||||
opts.PageSize = 10
|
||||
}
|
||||
|
||||
func (opts *SearchTeamOptions) toCond() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
|
||||
if len(opts.Keyword) > 0 {
|
||||
|
@ -117,10 +108,28 @@ func SearchTeam(opts *SearchTeamOptions) ([]*Team, int64, error) {
|
|||
cond = cond.And(keywordCond)
|
||||
}
|
||||
|
||||
cond = cond.And(builder.Eq{"org_id": opts.OrgID})
|
||||
if opts.OrgID > 0 {
|
||||
cond = cond.And(builder.Eq{"`team`.org_id": opts.OrgID})
|
||||
}
|
||||
|
||||
if opts.UserID > 0 {
|
||||
cond = cond.And(builder.Eq{"team_user.uid": opts.UserID})
|
||||
}
|
||||
|
||||
return cond
|
||||
}
|
||||
|
||||
// SearchTeam search for teams. Caller is responsible to check permissions.
|
||||
func SearchTeam(opts *SearchTeamOptions) ([]*Team, int64, error) {
|
||||
sess := db.GetEngine(db.DefaultContext)
|
||||
|
||||
opts.SetDefaultValues()
|
||||
cond := opts.toCond()
|
||||
|
||||
if opts.UserID > 0 {
|
||||
sess = sess.Join("INNER", "team_user", "team_user.team_id = team.id")
|
||||
}
|
||||
|
||||
count, err := sess.
|
||||
Where(cond).
|
||||
Count(new(Team))
|
||||
|
@ -128,7 +137,10 @@ func SearchTeam(opts *SearchTeamOptions) ([]*Team, int64, error) {
|
|||
return nil, 0, err
|
||||
}
|
||||
|
||||
sess = sess.Where(cond)
|
||||
if opts.UserID > 0 {
|
||||
sess = sess.Join("INNER", "team_user", "team_user.team_id = team.id")
|
||||
}
|
||||
|
||||
if opts.PageSize == -1 {
|
||||
opts.PageSize = int(count)
|
||||
} else {
|
||||
|
@ -137,6 +149,7 @@ func SearchTeam(opts *SearchTeamOptions) ([]*Team, int64, error) {
|
|||
|
||||
teams := make([]*Team, 0, opts.PageSize)
|
||||
if err = sess.
|
||||
Where(cond).
|
||||
OrderBy("lower_name").
|
||||
Find(&teams); err != nil {
|
||||
return nil, 0, err
|
||||
|
@ -346,3 +359,9 @@ func GetRepoTeams(ctx context.Context, repo *repo_model.Repository) (teams []*Te
|
|||
OrderBy("CASE WHEN name LIKE '" + OwnerTeamName + "' THEN '' ELSE name END").
|
||||
Find(&teams)
|
||||
}
|
||||
|
||||
// IncrTeamRepoNum increases the number of repos for the given team by 1
|
||||
func IncrTeamRepoNum(ctx context.Context, teamID int64) error {
|
||||
_, err := db.GetEngine(ctx).Incr("num_repos").ID(teamID).Update(new(Team))
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/packages/pub"
|
||||
"code.gitea.io/gitea/modules/packages/pypi"
|
||||
"code.gitea.io/gitea/modules/packages/rubygems"
|
||||
"code.gitea.io/gitea/modules/packages/vagrant"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
)
|
||||
|
@ -150,6 +151,8 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
|
|||
metadata = &pypi.Metadata{}
|
||||
case TypeRubyGems:
|
||||
metadata = &rubygems.Metadata{}
|
||||
case TypeVagrant:
|
||||
metadata = &vagrant.Metadata{}
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown package type: %s", string(p.Type)))
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ const (
|
|||
TypePub Type = "pub"
|
||||
TypePyPI Type = "pypi"
|
||||
TypeRubyGems Type = "rubygems"
|
||||
TypeVagrant Type = "vagrant"
|
||||
)
|
||||
|
||||
// Name gets the name of the package type
|
||||
|
@ -69,6 +70,8 @@ func (pt Type) Name() string {
|
|||
return "PyPI"
|
||||
case TypeRubyGems:
|
||||
return "RubyGems"
|
||||
case TypeVagrant:
|
||||
return "Vagrant"
|
||||
}
|
||||
panic(fmt.Sprintf("unknown package type: %s", string(pt)))
|
||||
}
|
||||
|
@ -98,6 +101,8 @@ func (pt Type) SVGName() string {
|
|||
return "gitea-python"
|
||||
case TypeRubyGems:
|
||||
return "gitea-rubygems"
|
||||
case TypeVagrant:
|
||||
return "gitea-vagrant"
|
||||
}
|
||||
panic(fmt.Sprintf("unknown package type: %s", string(pt)))
|
||||
}
|
||||
|
@ -219,9 +224,16 @@ func FindUnreferencedPackages(ctx context.Context) ([]*Package, error) {
|
|||
Find(&ps)
|
||||
}
|
||||
|
||||
// HasOwnerPackages tests if a user/org has packages
|
||||
// HasOwnerPackages tests if a user/org has accessible packages
|
||||
func HasOwnerPackages(ctx context.Context, ownerID int64) (bool, error) {
|
||||
return db.GetEngine(ctx).Where("owner_id = ?", ownerID).Exist(&Package{})
|
||||
return db.GetEngine(ctx).
|
||||
Table("package_version").
|
||||
Join("INNER", "package", "package.id = package_version.package_id").
|
||||
Where(builder.Eq{
|
||||
"package_version.is_internal": false,
|
||||
"package.owner_id": ownerID,
|
||||
}).
|
||||
Exist(&PackageVersion{})
|
||||
}
|
||||
|
||||
// HasRepositoryPackages tests if a repository has packages
|
||||
|
|
69
models/packages/package_test.go
Normal file
69
models/packages/package_test.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
// 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 packages_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
packages_model "code.gitea.io/gitea/models/packages"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
||||
_ "code.gitea.io/gitea/models"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m, &unittest.TestOptions{
|
||||
GiteaRootPath: filepath.Join("..", ".."),
|
||||
})
|
||||
}
|
||||
|
||||
func TestHasOwnerPackages(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
|
||||
p, err := packages_model.TryInsertPackage(db.DefaultContext, &packages_model.Package{
|
||||
OwnerID: owner.ID,
|
||||
LowerName: "package",
|
||||
})
|
||||
assert.NotNil(t, p)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// A package without package versions gets automatically cleaned up and should return false
|
||||
has, err := packages_model.HasOwnerPackages(db.DefaultContext, owner.ID)
|
||||
assert.False(t, has)
|
||||
assert.NoError(t, err)
|
||||
|
||||
pv, err := packages_model.GetOrInsertVersion(db.DefaultContext, &packages_model.PackageVersion{
|
||||
PackageID: p.ID,
|
||||
LowerVersion: "internal",
|
||||
IsInternal: true,
|
||||
})
|
||||
assert.NotNil(t, pv)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// A package with an internal package version gets automaticaly cleaned up and should return false
|
||||
has, err = packages_model.HasOwnerPackages(db.DefaultContext, owner.ID)
|
||||
assert.False(t, has)
|
||||
assert.NoError(t, err)
|
||||
|
||||
pv, err = packages_model.GetOrInsertVersion(db.DefaultContext, &packages_model.PackageVersion{
|
||||
PackageID: p.ID,
|
||||
LowerVersion: "normal",
|
||||
IsInternal: false,
|
||||
})
|
||||
assert.NotNil(t, pv)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// A package with a normal package version should return true
|
||||
has, err = packages_model.HasOwnerPackages(db.DefaultContext, owner.ID)
|
||||
assert.True(t, has)
|
||||
assert.NoError(t, err)
|
||||
}
|
|
@ -7,13 +7,10 @@ package access_test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
perm_model "code.gitea.io/gitea/models/perm"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
||||
|
@ -128,249 +125,3 @@ func TestRepository_RecalculateAccesses2(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.False(t, has)
|
||||
}
|
||||
|
||||
func TestRepoPermissionPublicNonOrgRepo(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
// public non-organization repo
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||
assert.NoError(t, repo.LoadUnits(db.DefaultContext))
|
||||
|
||||
// plain user
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.False(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
|
||||
// change to collaborator
|
||||
assert.NoError(t, models.AddCollaborator(repo, user))
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.True(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
|
||||
// collaborator
|
||||
collaborator := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, collaborator)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.True(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
|
||||
// owner
|
||||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.True(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
|
||||
// admin
|
||||
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.True(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoPermissionPrivateNonOrgRepo(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
// private non-organization repo
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
assert.NoError(t, repo.LoadUnits(db.DefaultContext))
|
||||
|
||||
// plain user
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
||||
perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.False(t, perm.CanRead(unit.Type))
|
||||
assert.False(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
|
||||
// change to collaborator to default write access
|
||||
assert.NoError(t, models.AddCollaborator(repo, user))
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.True(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
|
||||
assert.NoError(t, repo_model.ChangeCollaborationAccessMode(repo, user.ID, perm_model.AccessModeRead))
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.False(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
|
||||
// owner
|
||||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.True(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
|
||||
// admin
|
||||
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.True(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoPermissionPublicOrgRepo(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
// public organization repo
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 32})
|
||||
assert.NoError(t, repo.LoadUnits(db.DefaultContext))
|
||||
|
||||
// plain user
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
|
||||
perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.False(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
|
||||
// change to collaborator to default write access
|
||||
assert.NoError(t, models.AddCollaborator(repo, user))
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.True(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
|
||||
assert.NoError(t, repo_model.ChangeCollaborationAccessMode(repo, user.ID, perm_model.AccessModeRead))
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.False(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
|
||||
// org member team owner
|
||||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.True(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
|
||||
// org member team tester
|
||||
member := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15})
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, member)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
}
|
||||
assert.True(t, perm.CanWrite(unit.TypeIssues))
|
||||
assert.False(t, perm.CanWrite(unit.TypeCode))
|
||||
|
||||
// admin
|
||||
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.True(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepoPermissionPrivateOrgRepo(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
// private organization repo
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 24})
|
||||
assert.NoError(t, repo.LoadUnits(db.DefaultContext))
|
||||
|
||||
// plain user
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
|
||||
perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.False(t, perm.CanRead(unit.Type))
|
||||
assert.False(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
|
||||
// change to collaborator to default write access
|
||||
assert.NoError(t, models.AddCollaborator(repo, user))
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.True(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
|
||||
assert.NoError(t, repo_model.ChangeCollaborationAccessMode(repo, user.ID, perm_model.AccessModeRead))
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.False(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
|
||||
// org member team owner
|
||||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15})
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.True(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
|
||||
// update team information and then check permission
|
||||
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 5})
|
||||
err = organization.UpdateTeamUnits(team, nil)
|
||||
assert.NoError(t, err)
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.True(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
|
||||
// org member team tester
|
||||
tester := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, tester)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, perm.CanWrite(unit.TypeIssues))
|
||||
assert.False(t, perm.CanWrite(unit.TypeCode))
|
||||
assert.False(t, perm.CanRead(unit.TypeCode))
|
||||
|
||||
// org member team reviewer
|
||||
reviewer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 20})
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, reviewer)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, perm.CanRead(unit.TypeIssues))
|
||||
assert.False(t, perm.CanWrite(unit.TypeCode))
|
||||
assert.True(t, perm.CanRead(unit.TypeCode))
|
||||
|
||||
// admin
|
||||
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
|
||||
assert.NoError(t, err)
|
||||
for _, unit := range repo.Units {
|
||||
assert.True(t, perm.CanRead(unit.Type))
|
||||
assert.True(t, perm.CanWrite(unit.Type))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -430,3 +430,17 @@ func IsRepoReader(ctx context.Context, repo *repo_model.Repository, userID int64
|
|||
}
|
||||
return db.GetEngine(ctx).Where("repo_id = ? AND user_id = ? AND mode >= ?", repo.ID, userID, perm_model.AccessModeRead).Get(&Access{})
|
||||
}
|
||||
|
||||
// CheckRepoUnitUser check whether user could visit the unit of this repository
|
||||
func CheckRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *user_model.User, unitType unit.Type) bool {
|
||||
if user != nil && user.IsAdmin {
|
||||
return true
|
||||
}
|
||||
perm, err := GetUserRepoPermission(ctx, repo, user)
|
||||
if err != nil {
|
||||
log.Error("GetUserRepoPermission: %w", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return perm.CanRead(unitType)
|
||||
}
|
||||
|
|
172
models/repo.go
172
models/repo.go
|
@ -12,13 +12,13 @@ import (
|
|||
|
||||
_ "image/jpeg" // Needed for jpeg support
|
||||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
admin_model "code.gitea.io/gitea/models/admin"
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
project_model "code.gitea.io/gitea/models/project"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
|
@ -27,10 +27,7 @@ import (
|
|||
"code.gitea.io/gitea/models/webhook"
|
||||
"code.gitea.io/gitea/modules/lfs"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
@ -40,162 +37,6 @@ func NewRepoContext() {
|
|||
unit.LoadUnitConfig()
|
||||
}
|
||||
|
||||
// CheckRepoUnitUser check whether user could visit the unit of this repository
|
||||
func CheckRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *user_model.User, unitType unit.Type) bool {
|
||||
if user != nil && user.IsAdmin {
|
||||
return true
|
||||
}
|
||||
perm, err := access_model.GetUserRepoPermission(ctx, repo, user)
|
||||
if err != nil {
|
||||
log.Error("GetUserRepoPermission(): %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return perm.CanRead(unitType)
|
||||
}
|
||||
|
||||
// CreateRepoOptions contains the create repository options
|
||||
type CreateRepoOptions struct {
|
||||
Name string
|
||||
Description string
|
||||
OriginalURL string
|
||||
GitServiceType api.GitServiceType
|
||||
Gitignores string
|
||||
IssueLabels string
|
||||
License string
|
||||
Readme string
|
||||
DefaultBranch string
|
||||
IsPrivate bool
|
||||
IsMirror bool
|
||||
IsTemplate bool
|
||||
AutoInit bool
|
||||
Status repo_model.RepositoryStatus
|
||||
TrustModel repo_model.TrustModelType
|
||||
MirrorInterval string
|
||||
}
|
||||
|
||||
// CreateRepository creates a repository for the user/organization.
|
||||
func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, overwriteOrAdopt bool) (err error) {
|
||||
if err = repo_model.IsUsableRepoName(repo.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
has, err := repo_model.IsRepositoryExist(ctx, u, repo.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("IsRepositoryExist: %v", err)
|
||||
} else if has {
|
||||
return repo_model.ErrRepoAlreadyExist{
|
||||
Uname: u.Name,
|
||||
Name: repo.Name,
|
||||
}
|
||||
}
|
||||
|
||||
repoPath := repo_model.RepoPath(u.Name, repo.Name)
|
||||
isExist, err := util.IsExist(repoPath)
|
||||
if err != nil {
|
||||
log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
|
||||
return err
|
||||
}
|
||||
if !overwriteOrAdopt && isExist {
|
||||
log.Error("Files already exist in %s and we are not going to adopt or delete.", repoPath)
|
||||
return repo_model.ErrRepoFilesAlreadyExist{
|
||||
Uname: u.Name,
|
||||
Name: repo.Name,
|
||||
}
|
||||
}
|
||||
|
||||
if err = db.Insert(ctx, repo); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = repo_model.DeleteRedirect(ctx, u.ID, repo.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// insert units for repo
|
||||
units := make([]repo_model.RepoUnit, 0, len(unit.DefaultRepoUnits))
|
||||
for _, tp := range unit.DefaultRepoUnits {
|
||||
if tp == unit.TypeIssues {
|
||||
units = append(units, repo_model.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: tp,
|
||||
Config: &repo_model.IssuesConfig{
|
||||
EnableTimetracker: setting.Service.DefaultEnableTimetracking,
|
||||
AllowOnlyContributorsToTrackTime: setting.Service.DefaultAllowOnlyContributorsToTrackTime,
|
||||
EnableDependencies: setting.Service.DefaultEnableDependencies,
|
||||
},
|
||||
})
|
||||
} else if tp == unit.TypePullRequests {
|
||||
units = append(units, repo_model.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: tp,
|
||||
Config: &repo_model.PullRequestsConfig{AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle), AllowRebaseUpdate: true},
|
||||
})
|
||||
} else {
|
||||
units = append(units, repo_model.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: tp,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if err = db.Insert(ctx, units); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remember visibility preference.
|
||||
u.LastRepoVisibility = repo.IsPrivate
|
||||
if err = user_model.UpdateUserCols(ctx, u, "last_repo_visibility"); err != nil {
|
||||
return fmt.Errorf("updateUser: %v", err)
|
||||
}
|
||||
|
||||
if _, err = db.GetEngine(ctx).Incr("num_repos").ID(u.ID).Update(new(user_model.User)); err != nil {
|
||||
return fmt.Errorf("increment user total_repos: %v", err)
|
||||
}
|
||||
u.NumRepos++
|
||||
|
||||
// Give access to all members in teams with access to all repositories.
|
||||
if u.IsOrganization() {
|
||||
teams, err := organization.FindOrgTeams(ctx, u.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("loadTeams: %v", err)
|
||||
}
|
||||
for _, t := range teams {
|
||||
if t.IncludesAllRepositories {
|
||||
if err := addRepository(ctx, t, repo); err != nil {
|
||||
return fmt.Errorf("addRepository: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isAdmin, err := access_model.IsUserRepoAdmin(ctx, repo, doer); err != nil {
|
||||
return fmt.Errorf("IsUserRepoAdminCtx: %v", err)
|
||||
} else if !isAdmin {
|
||||
// Make creator repo admin if it wasn't assigned automatically
|
||||
if err = addCollaborator(ctx, repo, doer); err != nil {
|
||||
return fmt.Errorf("AddCollaborator: %v", err)
|
||||
}
|
||||
if err = repo_model.ChangeCollaborationAccessModeCtx(ctx, repo, doer.ID, perm.AccessModeAdmin); err != nil {
|
||||
return fmt.Errorf("ChangeCollaborationAccessMode: %v", err)
|
||||
}
|
||||
}
|
||||
} else if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
|
||||
// Organization automatically called this in addRepository method.
|
||||
return fmt.Errorf("recalculateAccesses: %v", err)
|
||||
}
|
||||
|
||||
if setting.Service.AutoWatchNewRepos {
|
||||
if err = repo_model.WatchRepo(ctx, doer.ID, repo.ID, true); err != nil {
|
||||
return fmt.Errorf("watchRepo: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = webhook.CopyDefaultWebhooksToRepo(ctx, repo.ID); err != nil {
|
||||
return fmt.Errorf("copyDefaultWebhooksToRepo: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteRepository deletes a repository for a user or organization.
|
||||
// make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock)
|
||||
func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
|
||||
|
@ -279,7 +120,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
|
|||
|
||||
if err := db.DeleteBeans(ctx,
|
||||
&access_model.Access{RepoID: repo.ID},
|
||||
&Action{RepoID: repo.ID},
|
||||
&activities_model.Action{RepoID: repo.ID},
|
||||
&repo_model.Collaboration{RepoID: repoID},
|
||||
&issues_model.Comment{RefRepoID: repoID},
|
||||
&git_model.CommitStatus{RepoID: repoID},
|
||||
|
@ -289,16 +130,16 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
|
|||
&repo_model.LanguageStat{RepoID: repoID},
|
||||
&issues_model.Milestone{RepoID: repoID},
|
||||
&repo_model.Mirror{RepoID: repoID},
|
||||
&Notification{RepoID: repoID},
|
||||
&activities_model.Notification{RepoID: repoID},
|
||||
&git_model.ProtectedBranch{RepoID: repoID},
|
||||
&git_model.ProtectedTag{RepoID: repoID},
|
||||
&repo_model.PushMirror{RepoID: repoID},
|
||||
&Release{RepoID: repoID},
|
||||
&repo_model.Release{RepoID: repoID},
|
||||
&repo_model.RepoIndexerStatus{RepoID: repoID},
|
||||
&repo_model.Redirect{RedirectRepoID: repoID},
|
||||
&repo_model.RepoUnit{RepoID: repoID},
|
||||
&repo_model.Star{RepoID: repoID},
|
||||
&Task{RepoID: repoID},
|
||||
&admin_model.Task{RepoID: repoID},
|
||||
&repo_model.Watch{RepoID: repoID},
|
||||
&webhook.Webhook{RepoID: repoID},
|
||||
); err != nil {
|
||||
|
@ -377,8 +218,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
|
|||
|
||||
archivePaths := make([]string, 0, len(archives))
|
||||
for _, v := range archives {
|
||||
p, _ := v.RelativePath()
|
||||
archivePaths = append(archivePaths, p)
|
||||
archivePaths = append(archivePaths, v.RelativePath())
|
||||
}
|
||||
|
||||
if _, err := db.DeleteByBean(ctx, &repo_model.RepoArchiver{RepoID: repoID}); err != nil {
|
||||
|
|
|
@ -39,9 +39,9 @@ func init() {
|
|||
db.RegisterModel(new(RepoArchiver))
|
||||
}
|
||||
|
||||
// RelativePath returns relative path
|
||||
func (archiver *RepoArchiver) RelativePath() (string, error) {
|
||||
return fmt.Sprintf("%d/%s/%s.%s", archiver.RepoID, archiver.CommitID[:2], archiver.CommitID, archiver.Type.String()), nil
|
||||
// RelativePath returns the archive path relative to the archive storage root.
|
||||
func (archiver *RepoArchiver) RelativePath() string {
|
||||
return fmt.Sprintf("%d/%s/%s.%s", archiver.RepoID, archiver.CommitID[:2], archiver.CommitID, archiver.Type.String())
|
||||
}
|
||||
|
||||
var delRepoArchiver = new(RepoArchiver)
|
||||
|
|
|
@ -8,7 +8,6 @@ package repo
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
|
@ -108,12 +107,14 @@ func DeleteMirrorByRepoID(repoID int64) error {
|
|||
|
||||
// MirrorsIterate iterates all mirror repositories.
|
||||
func MirrorsIterate(limit int, f func(idx int, bean interface{}) error) error {
|
||||
return db.GetEngine(db.DefaultContext).
|
||||
sess := db.GetEngine(db.DefaultContext).
|
||||
Where("next_update_unix<=?", time.Now().Unix()).
|
||||
And("next_update_unix!=0").
|
||||
OrderBy("updated_unix ASC").
|
||||
Limit(limit).
|
||||
Iterate(new(Mirror), f)
|
||||
OrderBy("updated_unix ASC")
|
||||
if limit > 0 {
|
||||
sess = sess.Limit(limit)
|
||||
}
|
||||
return sess.Iterate(new(Mirror), f)
|
||||
}
|
||||
|
||||
// InsertMirror inserts a mirror to database
|
||||
|
@ -121,55 +122,3 @@ func InsertMirror(ctx context.Context, mirror *Mirror) error {
|
|||
_, err := db.GetEngine(ctx).Insert(mirror)
|
||||
return err
|
||||
}
|
||||
|
||||
// MirrorRepositoryList contains the mirror repositories
|
||||
type MirrorRepositoryList []*Repository
|
||||
|
||||
func (repos MirrorRepositoryList) loadAttributes(ctx context.Context) error {
|
||||
if len(repos) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load mirrors.
|
||||
repoIDs := make([]int64, 0, len(repos))
|
||||
for i := range repos {
|
||||
if !repos[i].IsMirror {
|
||||
continue
|
||||
}
|
||||
|
||||
repoIDs = append(repoIDs, repos[i].ID)
|
||||
}
|
||||
mirrors := make([]*Mirror, 0, len(repoIDs))
|
||||
if err := db.GetEngine(ctx).
|
||||
Where("id > 0").
|
||||
In("repo_id", repoIDs).
|
||||
Find(&mirrors); err != nil {
|
||||
return fmt.Errorf("find mirrors: %v", err)
|
||||
}
|
||||
|
||||
set := make(map[int64]*Mirror)
|
||||
for i := range mirrors {
|
||||
set[mirrors[i].RepoID] = mirrors[i]
|
||||
}
|
||||
for i := range repos {
|
||||
repos[i].Mirror = set[repos[i].ID]
|
||||
if repos[i].Mirror != nil {
|
||||
repos[i].Mirror.Repo = repos[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadAttributes loads the attributes for the given MirrorRepositoryList
|
||||
func (repos MirrorRepositoryList) LoadAttributes() error {
|
||||
return repos.loadAttributes(db.DefaultContext)
|
||||
}
|
||||
|
||||
// GetUserMirrorRepositories returns a list of mirror repositories of given user.
|
||||
func GetUserMirrorRepositories(userID int64) ([]*Repository, error) {
|
||||
repos := make([]*Repository, 0, 10)
|
||||
return repos, db.GetEngine(db.DefaultContext).
|
||||
Where("owner_id = ?", userID).
|
||||
And("is_mirror = ?", true).
|
||||
Find(&repos)
|
||||
}
|
||||
|
|
|
@ -129,10 +129,12 @@ func GetPushMirrorsSyncedOnCommit(repoID int64) ([]*PushMirror, error) {
|
|||
|
||||
// PushMirrorsIterate iterates all push-mirror repositories.
|
||||
func PushMirrorsIterate(ctx context.Context, limit int, f func(idx int, bean interface{}) error) error {
|
||||
return db.GetEngine(ctx).
|
||||
sess := db.GetEngine(ctx).
|
||||
Where("last_update + (`interval` / ?) <= ?", time.Second, time.Now().Unix()).
|
||||
And("`interval` != 0").
|
||||
OrderBy("last_update ASC").
|
||||
Limit(limit).
|
||||
Iterate(new(PushMirror), f)
|
||||
OrderBy("last_update ASC")
|
||||
if limit > 0 {
|
||||
sess = sess.Limit(limit)
|
||||
}
|
||||
return sess.Iterate(new(PushMirror), f)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -14,7 +14,6 @@ import (
|
|||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
@ -23,14 +22,45 @@ import (
|
|||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// ErrReleaseAlreadyExist represents a "ReleaseAlreadyExist" kind of error.
|
||||
type ErrReleaseAlreadyExist struct {
|
||||
TagName string
|
||||
}
|
||||
|
||||
// IsErrReleaseAlreadyExist checks if an error is a ErrReleaseAlreadyExist.
|
||||
func IsErrReleaseAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrReleaseAlreadyExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrReleaseAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("release tag already exist [tag_name: %s]", err.TagName)
|
||||
}
|
||||
|
||||
// ErrReleaseNotExist represents a "ReleaseNotExist" kind of error.
|
||||
type ErrReleaseNotExist struct {
|
||||
ID int64
|
||||
TagName string
|
||||
}
|
||||
|
||||
// IsErrReleaseNotExist checks if an error is a ErrReleaseNotExist.
|
||||
func IsErrReleaseNotExist(err error) bool {
|
||||
_, ok := err.(ErrReleaseNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrReleaseNotExist) Error() string {
|
||||
return fmt.Sprintf("release tag does not exist [id: %d, tag_name: %s]", err.ID, err.TagName)
|
||||
}
|
||||
|
||||
// Release represents a release of repository.
|
||||
type Release struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"INDEX UNIQUE(n)"`
|
||||
Repo *repo_model.Repository `xorm:"-"`
|
||||
PublisherID int64 `xorm:"INDEX"`
|
||||
Publisher *user_model.User `xorm:"-"`
|
||||
TagName string `xorm:"INDEX UNIQUE(n)"`
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"INDEX UNIQUE(n)"`
|
||||
Repo *Repository `xorm:"-"`
|
||||
PublisherID int64 `xorm:"INDEX"`
|
||||
Publisher *user_model.User `xorm:"-"`
|
||||
TagName string `xorm:"INDEX UNIQUE(n)"`
|
||||
OriginalAuthor string
|
||||
OriginalAuthorID int64 `xorm:"index"`
|
||||
LowerTagName string
|
||||
|
@ -38,14 +68,14 @@ type Release struct {
|
|||
Title string
|
||||
Sha1 string `xorm:"VARCHAR(40)"`
|
||||
NumCommits int64
|
||||
NumCommitsBehind int64 `xorm:"-"`
|
||||
Note string `xorm:"TEXT"`
|
||||
RenderedNote string `xorm:"-"`
|
||||
IsDraft bool `xorm:"NOT NULL DEFAULT false"`
|
||||
IsPrerelease bool `xorm:"NOT NULL DEFAULT false"`
|
||||
IsTag bool `xorm:"NOT NULL DEFAULT false"`
|
||||
Attachments []*repo_model.Attachment `xorm:"-"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX"`
|
||||
NumCommitsBehind int64 `xorm:"-"`
|
||||
Note string `xorm:"TEXT"`
|
||||
RenderedNote string `xorm:"-"`
|
||||
IsDraft bool `xorm:"NOT NULL DEFAULT false"`
|
||||
IsPrerelease bool `xorm:"NOT NULL DEFAULT false"`
|
||||
IsTag bool `xorm:"NOT NULL DEFAULT false"`
|
||||
Attachments []*Attachment `xorm:"-"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -55,7 +85,7 @@ func init() {
|
|||
func (r *Release) loadAttributes(ctx context.Context) error {
|
||||
var err error
|
||||
if r.Repo == nil {
|
||||
r.Repo, err = repo_model.GetRepositoryByIDCtx(ctx, r.RepoID)
|
||||
r.Repo, err = GetRepositoryByIDCtx(ctx, r.RepoID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -116,7 +146,7 @@ func UpdateRelease(ctx context.Context, rel *Release) error {
|
|||
// AddReleaseAttachments adds a release attachments
|
||||
func AddReleaseAttachments(ctx context.Context, releaseID int64, attachmentUUIDs []string) (err error) {
|
||||
// Check attachments
|
||||
attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, attachmentUUIDs)
|
||||
attachments, err := GetAttachmentsByUUIDs(ctx, attachmentUUIDs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetAttachmentsByUUIDs [uuids: %v]: %v", attachmentUUIDs, err)
|
||||
}
|
||||
|
@ -279,9 +309,9 @@ func GetReleaseAttachments(ctx context.Context, rels ...*Release) (err error) {
|
|||
|
||||
// Sort
|
||||
sortedRels := releaseMetaSearch{ID: make([]int64, len(rels)), Rel: make([]*Release, len(rels))}
|
||||
var attachments []*repo_model.Attachment
|
||||
var attachments []*Attachment
|
||||
for index, element := range rels {
|
||||
element.Attachments = []*repo_model.Attachment{}
|
||||
element.Attachments = []*Attachment{}
|
||||
sortedRels.ID[index] = element.ID
|
||||
sortedRels.Rel[index] = element
|
||||
}
|
||||
|
@ -291,7 +321,7 @@ func GetReleaseAttachments(ctx context.Context, rels ...*Release) (err error) {
|
|||
err = db.GetEngine(ctx).
|
||||
Asc("release_id", "name").
|
||||
In("release_id", sortedRels.ID).
|
||||
Find(&attachments, repo_model.Attachment{})
|
||||
Find(&attachments, Attachment{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -354,7 +384,7 @@ func UpdateReleasesMigrationsByType(gitServiceType structs.GitServiceType, origi
|
|||
}
|
||||
|
||||
// PushUpdateDeleteTagsContext updates a number of delete tags with context
|
||||
func PushUpdateDeleteTagsContext(ctx context.Context, repo *repo_model.Repository, tags []string) error {
|
||||
func PushUpdateDeleteTagsContext(ctx context.Context, repo *Repository, tags []string) error {
|
||||
if len(tags) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
@ -384,7 +414,7 @@ func PushUpdateDeleteTagsContext(ctx context.Context, repo *repo_model.Repositor
|
|||
}
|
||||
|
||||
// PushUpdateDeleteTag must be called for any push actions to delete tag
|
||||
func PushUpdateDeleteTag(repo *repo_model.Repository, tagName string) error {
|
||||
func PushUpdateDeleteTag(repo *Repository, tagName string) error {
|
||||
rel, err := GetRelease(repo.ID, tagName)
|
||||
if err != nil {
|
||||
if IsErrReleaseNotExist(err) {
|
||||
|
@ -409,7 +439,7 @@ func PushUpdateDeleteTag(repo *repo_model.Repository, tagName string) error {
|
|||
}
|
||||
|
||||
// SaveOrUpdateTag must be called for any push actions to add tag
|
||||
func SaveOrUpdateTag(repo *repo_model.Repository, newRel *Release) error {
|
||||
func SaveOrUpdateTag(repo *Repository, newRel *Release) error {
|
||||
rel, err := GetRelease(repo.ID, newRel.TagName)
|
||||
if err != nil && !IsErrReleaseNotExist(err) {
|
||||
return fmt.Errorf("GetRelease: %v", err)
|
|
@ -23,6 +23,8 @@ import (
|
|||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// ErrUserDoesNotHaveAccessToRepo represets an error where the user doesn't has access to a given repo.
|
||||
|
@ -784,3 +786,15 @@ func UpdateRepoIssueNumbers(ctx context.Context, repoID int64, isPull, isClosed
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CountNullArchivedRepository counts the number of repositories with is_archived is null
|
||||
func CountNullArchivedRepository() (int64, error) {
|
||||
return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Count(new(Repository))
|
||||
}
|
||||
|
||||
// FixNullArchivedRepository sets is_archived to false where it is null
|
||||
func FixNullArchivedRepository() (int64, error) {
|
||||
return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Cols("is_archived").NoAutoTime().Update(&Repository{
|
||||
IsArchived: false,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -163,6 +163,10 @@ type SearchRepoOptions struct {
|
|||
HasMilestones util.OptionalBool
|
||||
// LowerNames represents valid lower names to restrict to
|
||||
LowerNames []string
|
||||
// When specified true, apply some filters over the conditions:
|
||||
// - Don't show forks, when opts.Fork is OptionalBoolNone.
|
||||
// - Do not display repositories that don't have a description, an icon and topics.
|
||||
OnlyShowRelevant bool
|
||||
}
|
||||
|
||||
// SearchOrderBy is used to sort the result
|
||||
|
@ -463,8 +467,12 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
|
|||
Where(builder.Eq{"language": opts.Language}).And(builder.Eq{"is_primary": true})))
|
||||
}
|
||||
|
||||
if opts.Fork != util.OptionalBoolNone {
|
||||
cond = cond.And(builder.Eq{"is_fork": opts.Fork == util.OptionalBoolTrue})
|
||||
if opts.Fork != util.OptionalBoolNone || opts.OnlyShowRelevant {
|
||||
if opts.OnlyShowRelevant && opts.Fork == util.OptionalBoolNone {
|
||||
cond = cond.And(builder.Eq{"is_fork": false})
|
||||
} else {
|
||||
cond = cond.And(builder.Eq{"is_fork": opts.Fork == util.OptionalBoolTrue})
|
||||
}
|
||||
}
|
||||
|
||||
if opts.Mirror != util.OptionalBoolNone {
|
||||
|
@ -486,6 +494,25 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
|
|||
cond = cond.And(builder.Eq{"num_milestones": 0}.Or(builder.IsNull{"num_milestones"}))
|
||||
}
|
||||
|
||||
if opts.OnlyShowRelevant {
|
||||
// Only show a repo that either has a topic or description.
|
||||
subQueryCond := builder.NewCond()
|
||||
|
||||
// Topic checking. Topics is non-null.
|
||||
subQueryCond = subQueryCond.Or(builder.And(builder.Neq{"topics": "null"}, builder.Neq{"topics": "[]"}))
|
||||
|
||||
// Description checking. Description not empty.
|
||||
subQueryCond = subQueryCond.Or(builder.Neq{"description": ""})
|
||||
|
||||
// Repo has a avatar.
|
||||
subQueryCond = subQueryCond.Or(builder.Neq{"avatar": ""})
|
||||
|
||||
// Always hide repo's that are empty.
|
||||
subQueryCond = subQueryCond.And(builder.Eq{"is_empty": false})
|
||||
|
||||
cond = cond.And(subQueryCond)
|
||||
}
|
||||
|
||||
return cond
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
package repo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -20,13 +20,21 @@ import (
|
|||
gouuid "github.com/google/uuid"
|
||||
)
|
||||
|
||||
// ____ ___ .__ .___ ___________.___.__
|
||||
// | | \______ | | _________ __| _/ \_ _____/| | | ____ ______
|
||||
// | | /\____ \| | / _ \__ \ / __ | | __) | | | _/ __ \ / ___/
|
||||
// | | / | |_> > |_( <_> ) __ \_/ /_/ | | \ | | |_\ ___/ \___ \
|
||||
// |______/ | __/|____/\____(____ /\____ | \___ / |___|____/\___ >____ >
|
||||
// |__| \/ \/ \/ \/ \/
|
||||
//
|
||||
// ErrUploadNotExist represents a "UploadNotExist" kind of error.
|
||||
type ErrUploadNotExist struct {
|
||||
ID int64
|
||||
UUID string
|
||||
}
|
||||
|
||||
// IsErrUploadNotExist checks if an error is a ErrUploadNotExist.
|
||||
func IsErrUploadNotExist(err error) bool {
|
||||
_, ok := err.(ErrUploadNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUploadNotExist) Error() string {
|
||||
return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID)
|
||||
}
|
||||
|
||||
// Upload represent a uploaded file to a repo to be deleted when moved
|
||||
type Upload struct {
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue