merge main branch

This commit is contained in:
Lunny Xiao 2022-09-06 17:12:34 +08:00
commit 9740a6dbbb
No known key found for this signature in database
GPG key ID: C3B7C91B632F738A
1184 changed files with 14955 additions and 10799 deletions

View file

@ -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
View file

@ -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/

View file

@ -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

View file

@ -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.

View file

@ -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
View file

@ -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

File diff suppressed because one or more lines are too long

View file

@ -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`))

View file

@ -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
`

View 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);

View file

@ -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)

View file

@ -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)
}
}
}

View file

@ -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
}

View file

@ -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("")

View file

@ -123,7 +123,7 @@ func initEmbeddedExtractor(c *cli.Context) error {
sections["public"] = &section{Path: "public", Names: public.AssetNames, IsDir: public.AssetIsDir, Asset: public.Asset}
sections["options"] = &section{Path: "options", Names: options.AssetNames, IsDir: options.AssetIsDir, Asset: options.Asset}
sections["templates"] = &section{Path: "templates", Names: templates.AssetNames, IsDir: templates.AssetIsDir, Asset: templates.Asset}
sections["templates"] = &section{Path: "templates", Names: templates.BuiltinAssetNames, IsDir: templates.BuiltinAssetIsDir, Asset: templates.BuiltinAsset}
for _, sec := range sections {
assets = append(assets, buildAssetList(sec, pats, c)...)

View file

@ -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
})
}

View file

@ -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,

View file

@ -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)
}

View file

@ -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) {

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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")

View file

@ -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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -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
View file

@ -2,3 +2,6 @@ public/
templates/swagger/v1_json.tmpl
themes/
resources/
# Temporary lock file while building
/.hugo_build.lock

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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":"..."}]
```

View file

@ -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.

View file

@ -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

View file

@ -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

View 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/)

View file

@ -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:

View file

@ -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!**

View 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
```

View 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"
```

View file

@ -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`)

View file

@ -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]

View file

@ -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 | - |

View 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
View file

@ -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

642
go.sum

File diff suppressed because it is too large Load diff

View file

@ -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: {

View file

@ -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
}

View file

@ -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"

View file

@ -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{})
}

View 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("..", ".."),
})
}

View file

@ -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
}

View file

@ -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})
}

View file

@ -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
}

View file

@ -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))

View file

@ -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
}

View file

@ -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)

View file

@ -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"},
})
}

View file

@ -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})
}

View file

@ -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"

View file

@ -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)

View file

@ -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"`

View file

@ -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",
},
})
}

View file

@ -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())
}

View file

@ -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{

View file

@ -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)

View file

@ -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))
}

View file

@ -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})
}

View file

@ -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
}

View file

@ -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"`

View file

@ -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)
}

View file

@ -63,3 +63,9 @@
uid: 29
org_id: 17
is_public: true
-
id: 12
uid: 2
org_id: 17
is_public: true

View file

@ -309,7 +309,7 @@
avatar_email: user17@example.com
num_repos: 2
is_active: true
num_members: 3
num_members: 4
num_teams: 3
-

View file

@ -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

View file

@ -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) {

View file

@ -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
}

View file

@ -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},
}

View file

@ -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

View file

@ -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)

View file

@ -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")
}

View file

@ -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 {

View file

@ -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
View 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()
}

View file

@ -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)

View file

@ -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 {

View file

@ -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())

View 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
}

View 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)
}

View file

@ -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
}

View file

@ -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)))
}

View file

@ -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

View 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)
}

View file

@ -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))
}
}

View file

@ -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)
}

View file

@ -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 {

View file

@ -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)

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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)

View file

@ -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,
})
}

View file

@ -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
}

View file

@ -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