Compare commits

..

17 Commits

Author SHA1 Message Date
Lunny Xiao
bbe6aa349f fix bug not to trim space of login username (#1806) 2017-05-29 06:10:11 -05:00
agrn
4fd55d8796 Backport bugfixes #1220 and #1393 to v1.1 (#1758)
* Fix "Error: No issue number specified"  when pushing

* fix wrong log when push to a new branch (#1220)

* Removing empty file modules/github/issues.go
2017-05-20 12:43:43 +08:00
Jonas Östanbäck
daaabaa1d9 Enforce netgo build tag while cross-compilation (Backport of #1690) (#1731) 2017-05-18 09:04:12 +08:00
Lunny Xiao
fa059debca fix delete user failed on sqlite (#1321) 2017-05-17 12:43:22 +02:00
Kim "BKC" Carlbäcker
2854c8aa47 Merge pull request #1724 from lunny/lunny/fix_avatar_update
fix update avatar
2017-05-15 17:16:31 +02:00
Lunny Xiao
506c98df5b fix update avatar 2017-05-14 13:39:53 +08:00
Kim "BKC" Carlbäcker
f9859a2991 Update changelog with v1.1.1 2017-05-04 18:29:26 +02:00
Kim "BKC" Carlbäcker
473df53533 [backport] fix sanitation (#1646)
* Santiation fix from Gogs

- Add code-injection checks

* Fix SSH-breakage
2017-04-30 17:55:16 +08:00
Philippe Kueck
2482c67e2b backport of PR #1257 for release/v1.1: rewrite pre-commit, post-commit and options hooks (#1376)
* issue #1250, replace {pre,post}-receive and update hooks with a single shell script that does not require custom hooks to be a sh-script

* issue #1250, make script posix compilant

* v23, add migration script to update {pre,post}-receive and update hooks

* migration: use a more common name and rename v23 to v26 to avoid conflicts

* gofmt'ed and added copyright header

* fix SyncRepositoryHooks to also sync wiki repos

* no migration for you.
2017-03-28 15:47:59 +08:00
Lunny Xiao
d9bdf7a65d fix migration failed when authorized_keys is not exist (#1375)
* fix wiki bugs (#1294)

* fix migration failed when authorized_keys is not exist
2017-03-23 23:07:30 +08:00
Lunny Xiao
11ad296347 fix wiki bugs (#1294) (#1338) 2017-03-20 22:54:33 +08:00
Lunny Xiao
5fad54248f fix forget migration for wiki hooks (#1227) (#1237) 2017-03-17 13:01:33 +08:00
Philippe Kueck
2b5e4b4d96 fix #1189, commit messages containing a pipe (#1218)
(d680164f11)
2017-03-16 19:41:19 +08:00
Lunny Xiao
65eea82c3e fix repo settings external tracker failed and check external urls (#1215) (#1236) 2017-03-13 16:23:00 +08:00
Lunny Xiao
fac75b8086 Fix upgrade failed after ever rollback (#1194) (#1197)
* fix upgrade failed after rollback

* fix upgrade failed after rollback

* fix possible upgrade twice fail after rollback
2017-03-11 17:01:22 +08:00
Lunny Xiao
e4706127f9 bug fixed for delete repo failed (#1193) (#1195) 2017-03-10 22:28:00 +08:00
Lunny Xiao
c9baf9d14b fix ini format incomiptable with crowdin (#1177) (#1192) 2017-03-10 20:59:51 +08:00
8837 changed files with 442387 additions and 1355497 deletions

View File

@@ -1,44 +0,0 @@
repo: go-gitea/gitea
groups:
-
name: BREAKING
labels:
- kind/breaking
-
name: FEATURE
labels:
- kind/feature
-
name: BUGFIXES
labels:
- kind/bug
-
name: ENHANCEMENT
labels:
- kind/enhancement
- kind/refactor
- kind/ui
-
name: SECURITY
labels:
- kind/security
-
name: TESTING
labels:
- kind/testing
-
name: TRANSLATION
labels:
- kind/translation
-
name: BUILD
labels:
- kind/build
- kind/lint
-
name: DOCS
labels:
- kind/docs
-
name: MISC
default: true

5
.dockerignore Normal file
View File

@@ -0,0 +1,5 @@
*
!gitea
!docker
!public
!templates

View File

@@ -1,773 +1,145 @@
---
kind: pipeline
name: testing
platform:
os: linux
arch: amd64
workspace:
base: /go
base: /srv/app
path: src/code.gitea.io/gitea
pipeline:
clone:
image: plugins/git
tags: true
test:
image: webhippie/golang:edge
pull: true
environment:
TAGS: bindata sqlite
GOPATH: /srv/app
commands:
- apk -U add openssh-client
- make clean
- make generate
- make vet
- make lint
- make test
- make build
when:
event: [ push, tag, pull_request ]
test-mysql:
image: webhippie/golang:edge
pull: true
environment:
TAGS: bindata
GOPATH: /srv/app
commands:
- make test-mysql
when:
event: [ push, tag, pull_request ]
test-pgsql:
image: webhippie/golang:edge
pull: true
environment:
TAGS: bindata
GOPATH: /srv/app
commands:
- make test-pgsql
when:
event: [ push, tag, pull_request ]
static:
image: karalabe/xgo-latest:latest
pull: true
environment:
TAGS: bindata sqlite
GOPATH: /srv/app
commands:
- make release
when:
event: [ push, tag, pull_request ]
coverage:
image: plugins/coverage
server: https://coverage.gitea.io
when:
event: [ push, tag, pull_request ]
docker:
image: plugins/docker
repo: gitea/gitea
tags: [ '${DRONE_TAG##v}' ]
when:
event: [ tag ]
branch: [ refs/tags/* ]
docker:
image: plugins/docker
repo: gitea/gitea
tags: [ '${DRONE_BRANCH##release/v}' ]
when:
event: [ push ]
branch: [ release/* ]
docker:
image: plugins/docker
repo: gitea/gitea
tags: [ 'latest' ]
when:
event: [ push ]
branch: [ master ]
release:
image: plugins/s3
path_style: true
strip_prefix: dist/release/
source: dist/release/*
target: /gitea/${DRONE_TAG##v}
when:
event: [ tag ]
branch: [ refs/tags/* ]
release:
image: plugins/s3
path_style: true
strip_prefix: dist/release/
source: dist/release/*
target: /gitea/${DRONE_BRANCH##release/v}
when:
event: [ push ]
branch: [ release/* ]
release:
image: plugins/s3
path_style: true
strip_prefix: dist/release/
source: dist/release/*
target: /gitea/master
when:
event: [ push ]
branch: [ master ]
github:
image: plugins/github-release
files:
- dist/release/*
when:
event: [ tag ]
branch: [ refs/tags/* ]
gitter:
image: plugins/gitter
services:
- name: mysql
pull: default
mysql:
image: mysql:5.7
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: test
- MYSQL_DATABASE=test
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
when:
event: [ push, tag, pull_request ]
- name: mysql8
pull: default
image: mysql:8.0
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: testgitea
- name: pgsql
pull: default
pgsql:
image: postgres:9.5
environment:
POSTGRES_DB: test
- name: mssql
pull: default
image: microsoft/mssql-server-linux:latest
environment:
ACCEPT_EULA: Y
MSSQL_PID: Standard
SA_PASSWORD: MwantsaSecurePassword1
- name: ldap
pull: default
image: gitea/test-openldap:latest
steps:
- name: fetch-tags
pull: default
image: docker:git
commands:
- git fetch --tags --force
- POSTGRES_DB=test
when:
event:
exclude:
- pull_request
- name: pre-build
pull: always
image: node:10 # this step is kept at the lowest version of node that we support
commands:
- make css
- make js
- name: build-without-gcc
pull: always
image: golang:1.11 # this step is kept as the lowest version of golang that we support
environment:
GO111MODULE: on
GOPROXY: off
commands:
- curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt -y install nodejs
- go build -mod=vendor -o gitea_no_gcc # test if build succeeds without the sqlite tag
- name: build-linux-386
pull: always
image: golang:1.13
environment:
GO111MODULE: on
GOPROXY: off
GOOS: linux
GOARCH: 386
commands:
- curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt -y install nodejs
- go build -mod=vendor -o gitea_linux_386 # test if compatible with 32 bit
- name: build
pull: always
image: golang:1.13
commands:
- curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt -y install nodejs
- make clean
- make golangci-lint
- make revive
- make swagger-check
- make swagger-validate
- make test-vendor
- make build
environment:
GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not
GOSUMDB: sum.golang.org
TAGS: bindata sqlite sqlite_unlock_notify
- name: unit-test
pull: always
image: golang:1.13
commands:
- make unit-test-coverage
environment:
GOPROXY: off
TAGS: bindata sqlite sqlite_unlock_notify
depends_on:
- build
when:
branch:
- master
event:
- push
- pull_request
- name: release-test
pull: always
image: golang:1.13
commands:
- make test
environment:
GOPROXY: off
TAGS: bindata sqlite sqlite_unlock_notify
depends_on:
- build
when:
branch:
- "release/*"
event:
- push
- pull_request
- name: tag-pre-condition
pull: always
image: alpine/git
commands:
- git update-ref refs/heads/tag_test ${DRONE_COMMIT_SHA}
depends_on:
- build
when:
event:
- tag
- name: tag-test
pull: always
image: golang:1.13
commands:
- make test
environment:
GOPROXY: off
TAGS: bindata
depends_on:
- tag-pre-condition
when:
event:
- tag
- name: test-sqlite
pull: always
image: golang:1.13
commands:
- "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash"
- apt-get install -y git-lfs
- timeout -s ABRT 20m make test-sqlite-migration
- timeout -s ABRT 20m make test-sqlite
environment:
GOPROXY: off
TAGS: bindata
depends_on:
- build
- name: test-mysql
pull: always
image: golang:1.13
commands:
- "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash"
- apt-get install -y git-lfs
- make test-mysql-migration
- make integration-test-coverage
environment:
GOPROXY: off
TAGS: bindata
TEST_LDAP: 1
depends_on:
- build
when:
branch:
- master
event:
- push
- pull_request
- name: tag-test-mysql
pull: always
image: golang:1.13
commands:
- "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash"
- apt-get install -y git-lfs
- timeout -s ABRT 20m make test-mysql-migration
- timeout -s ABRT 20m make test-mysql
environment:
GOPROXY: off
TAGS: bindata
TEST_LDAP: 1
depends_on:
- build
when:
event:
- tag
- name: test-mysql8
pull: always
image: golang:1.13
commands:
- "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash"
- apt-get install -y git-lfs
- timeout -s ABRT 20m make test-mysql8-migration
- timeout -s ABRT 20m make test-mysql8
environment:
GOPROXY: off
TAGS: bindata
TEST_LDAP: 1
depends_on:
- build
- name: test-pgsql
pull: always
image: golang:1.13
commands:
- "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash"
- apt-get install -y git-lfs
- timeout -s ABRT 20m make test-pgsql-migration
- timeout -s ABRT 20m make test-pgsql
environment:
GOPROXY: off
TAGS: bindata
TEST_LDAP: 1
depends_on:
- build
- name: test-mssql
pull: always
image: golang:1.13
commands:
- "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash"
- apt-get install -y git-lfs
- make test-mssql-migration
- make test-mssql
environment:
GOPROXY: off
TAGS: bindata
TEST_LDAP: 1
depends_on:
- build
- name: generate-coverage
pull: always
image: golang:1.13
commands:
- make coverage
environment:
GOPROXY: off
TAGS: bindata
depends_on:
- unit-test
- test-mysql
when:
branch:
- master
event:
- push
- pull_request
- name: coverage
pull: always
image: robertstettner/drone-codecov
settings:
files:
- coverage.all
environment:
CODECOV_TOKEN:
from_secret: codecov_token
depends_on:
- generate-coverage
when:
branch:
- master
event:
- push
- pull_request
---
kind: pipeline
name: translations
platform:
os: linux
arch: amd64
workspace:
base: /go
path: src/code.gitea.io/gitea
trigger:
branch:
- master
event:
- push
steps:
- name: download
pull: always
image: jonasfranz/crowdin
settings:
download: true
export_dir: options/locale/
ignore_branch: true
project_identifier: gitea
environment:
CROWDIN_KEY:
from_secret: crowdin_key
- name: update
pull: default
image: alpine:3.11
commands:
- mv ./options/locale/locale_en-US.ini ./options/
- "sed -i -e 's/=\"/=/g' -e 's/\"$$//g' ./options/locale/*.ini"
- "sed -i -e 's/\\\\\\\\\"/\"/g' ./options/locale/*.ini"
- mv ./options/locale_en-US.ini ./options/locale/
- name: push
pull: always
image: appleboy/drone-git-push
settings:
author_email: "teabot@gitea.io"
author_name: GiteaBot
commit: true
commit_message: "[skip ci] Updated translations via Crowdin"
remote: "git@github.com:go-gitea/gitea.git"
environment:
GIT_PUSH_SSH_KEY:
from_secret: git_push_ssh_key
- name: upload_translations
pull: always
image: jonasfranz/crowdin
settings:
files:
locale_en-US.ini: options/locale/locale_en-US.ini
ignore_branch: true
project_identifier: gitea
environment:
CROWDIN_KEY:
from_secret: crowdin_key
---
kind: pipeline
name: release-master
platform:
os: linux
arch: amd64
workspace:
base: /go
path: src/code.gitea.io/gitea
trigger:
branch:
- master
- "release/*"
event:
- push
depends_on:
- testing
- translations
steps:
- name: fetch-tags
pull: default
image: docker:git
commands:
- git fetch --tags --force
- name: static
pull: always
image: techknowlogick/xgo:latest
commands:
- apt update && apt -y install curl
- curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt -y install nodejs
- export PATH=$PATH:$GOPATH/bin
- make release
environment:
GOPROXY: off
TAGS: bindata sqlite sqlite_unlock_notify
- name: gpg-sign
pull: always
image: plugins/gpgsign:1
settings:
detach_sign: true
excludes:
- "dist/release/*.sha256"
files:
- "dist/release/*"
environment:
GPGSIGN_KEY:
from_secret: gpgsign_key
GPGSIGN_PASSPHRASE:
from_secret: gpgsign_passphrase
- name: release-branch-release
pull: always
image: plugins/s3:1
settings:
acl: public-read
bucket: releases
endpoint: https://storage.gitea.io
path_style: true
source: "dist/release/*"
strip_prefix: dist/release/
target: "/gitea/${DRONE_BRANCH##release/v}"
environment:
AWS_ACCESS_KEY_ID:
from_secret: aws_access_key_id
AWS_SECRET_ACCESS_KEY:
from_secret: aws_secret_access_key
when:
branch:
- "release/*"
event:
- push
- name: release
pull: always
image: plugins/s3:1
settings:
acl: public-read
bucket: releases
endpoint: https://storage.gitea.io
path_style: true
source: "dist/release/*"
strip_prefix: dist/release/
target: /gitea/master
environment:
AWS_ACCESS_KEY_ID:
from_secret: aws_access_key_id
AWS_SECRET_ACCESS_KEY:
from_secret: aws_secret_access_key
when:
branch:
- master
event:
- push
---
kind: pipeline
name: release-version
platform:
os: linux
arch: amd64
workspace:
base: /go
path: src/code.gitea.io/gitea
trigger:
event:
- tag
depends_on:
- testing
steps:
- name: fetch-tags
pull: default
image: docker:git
commands:
- git fetch --tags --force
- name: static
pull: always
image: techknowlogick/xgo:latest
commands:
- apt update && apt -y install curl
- curl -sL https://deb.nodesource.com/setup_12.x | bash - && apt -y install nodejs
- export PATH=$PATH:$GOPATH/bin
- make release
environment:
GOPROXY: off
TAGS: bindata sqlite sqlite_unlock_notify
- name: gpg-sign
pull: always
image: plugins/gpgsign:1
settings:
detach_sign: true
excludes:
- "dist/release/*.sha256"
files:
- "dist/release/*"
environment:
GPGSIGN_KEY:
from_secret: gpgsign_key
GPGSIGN_PASSPHRASE:
from_secret: gpgsign_passphrase
- name: release
pull: always
image: plugins/s3:1
settings:
acl: public-read
bucket: releases
endpoint: https://storage.gitea.io
path_style: true
source: "dist/release/*"
strip_prefix: dist/release/
target: "/gitea/${DRONE_TAG##v}"
environment:
AWS_ACCESS_KEY_ID:
from_secret: aws_access_key_id
AWS_SECRET_ACCESS_KEY:
from_secret: aws_secret_access_key
- name: github
pull: always
image: plugins/github-release:1
settings:
files:
- "dist/release/*"
environment:
GITHUB_TOKEN:
from_secret: github_token
---
kind: pipeline
name: docs
platform:
os: linux
arch: amd64
workspace:
base: /go
path: src/code.gitea.io/gitea
steps:
- name: build-docs
pull: always
image: webhippie/hugo:latest
commands:
- cd docs
- make trans-copy
- make clean
- make build
- name: publish-docs
pull: always
image: lucap/drone-netlify:latest
settings:
path: docs/public/
site_id: d2260bae-7861-4c02-8646-8f6440b12672
environment:
NETLIFY_TOKEN:
from_secret: netlify_token
when:
branch:
- master
event:
- push
---
kind: pipeline
name: docker-linux-amd64
platform:
os: linux
arch: amd64
workspace:
base: /go
path: src/code.gitea.io/gitea
depends_on:
- testing
trigger:
ref:
- refs/heads/master
- "refs/tags/**"
- "refs/pull/**"
steps:
- name: fetch-tags
pull: default
image: docker:git
commands:
- git fetch --tags --force
when:
event:
exclude:
- pull_request
- name: dryrun
pull: always
image: plugins/docker:linux-amd64
settings:
dry_run: true
repo: gitea/gitea
tags: linux-amd64
build_args:
- GOPROXY=off
when:
event:
- pull_request
- name: publish
pull: always
image: plugins/docker:linux-amd64
settings:
auto_tag: true
auto_tag_suffix: linux-amd64
repo: gitea/gitea
build_args:
- GOPROXY=off
password:
from_secret: docker_password
username:
from_secret: docker_username
when:
event:
exclude:
- pull_request
---
kind: pipeline
name: docker-linux-arm64
platform:
os: linux
arch: arm64
workspace:
base: /go
path: src/code.gitea.io/gitea
depends_on:
- testing
trigger:
ref:
- refs/heads/master
- "refs/tags/**"
- "refs/pull/**"
steps:
- name: fetch-tags
pull: default
image: docker:git
commands:
- git fetch --tags --force
when:
event:
exclude:
- pull_request
- name: dryrun
pull: always
image: plugins/docker:linux-arm64
settings:
dry_run: true
repo: gitea/gitea
tags: linux-arm64
build_args:
- GOPROXY=off
when:
event:
- pull_request
- name: publish
pull: always
image: plugins/docker:linux-arm64
settings:
auto_tag: true
auto_tag_suffix: linux-arm64
repo: gitea/gitea
build_args:
- GOPROXY=off
password:
from_secret: docker_password
username:
from_secret: docker_username
when:
event:
exclude:
- pull_request
---
kind: pipeline
name: docker-manifest
platform:
os: linux
arch: amd64
steps:
- name: manifest
pull: always
image: plugins/manifest
settings:
auto_tag: true
ignore_missing: true
spec: docker/manifest.tmpl
password:
from_secret: docker_password
username:
from_secret: docker_username
trigger:
ref:
- refs/heads/master
- "refs/tags/**"
depends_on:
- docker-linux-amd64
- docker-linux-arm64
---
kind: pipeline
name: notify
platform:
os: linux
arch: amd64
workspace:
base: /go
path: src/code.gitea.io/gitea
clone:
disable: true
when:
status:
- success
- failure
depends_on:
- testing
- translations
- release-version
- release-master
- docker-linux-amd64
- docker-linux-arm64
- docker-manifest
- docs
steps:
- name: discord
pull: always
image: appleboy/drone-discord:1.0.0
environment:
DISCORD_WEBHOOK_ID:
from_secret: discord_webhook_id
DISCORD_WEBHOOK_TOKEN:
from_secret: discord_webhook_token
event: [ push, tag, pull_request ]

1
.drone.yml.sig Normal file
View File

@@ -0,0 +1 @@
eyJhbGciOiJIUzI1NiJ9.d29ya3NwYWNlOgogIGJhc2U6IC9zcnYvYXBwCiAgcGF0aDogc3JjL2NvZGUuZ2l0ZWEuaW8vZ2l0ZWEKCnBpcGVsaW5lOgogIGNsb25lOgogICAgaW1hZ2U6IHBsdWdpbnMvZ2l0CiAgICB0YWdzOiB0cnVlCgogIHRlc3Q6CiAgICBpbWFnZTogd2ViaGlwcGllL2dvbGFuZzplZGdlCiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgVEFHUzogYmluZGF0YSBzcWxpdGUKICAgICAgR09QQVRIOiAvc3J2L2FwcAogICAgY29tbWFuZHM6CiAgICAgIC0gYXBrIC1VIGFkZCBvcGVuc3NoLWNsaWVudAogICAgICAtIG1ha2UgY2xlYW4KICAgICAgLSBtYWtlIGdlbmVyYXRlCiAgICAgIC0gbWFrZSB2ZXQKICAgICAgLSBtYWtlIGxpbnQKICAgICAgLSBtYWtlIHRlc3QKICAgICAgLSBtYWtlIGJ1aWxkCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoLCB0YWcsIHB1bGxfcmVxdWVzdCBdCgogIHRlc3QtbXlzcWw6CiAgICBpbWFnZTogd2ViaGlwcGllL2dvbGFuZzplZGdlCiAgICBwdWxsOiB0cnVlCiAgICBlbnZpcm9ubWVudDoKICAgICAgVEFHUzogYmluZGF0YQogICAgICBHT1BBVEg6IC9zcnYvYXBwCiAgICBjb21tYW5kczoKICAgICAgLSBtYWtlIHRlc3QtbXlzcWwKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2gsIHRhZywgcHVsbF9yZXF1ZXN0IF0KCiAgdGVzdC1wZ3NxbDoKICAgIGltYWdlOiB3ZWJoaXBwaWUvZ29sYW5nOmVkZ2UKICAgIHB1bGw6IHRydWUKICAgIGVudmlyb25tZW50OgogICAgICBUQUdTOiBiaW5kYXRhCiAgICAgIEdPUEFUSDogL3Nydi9hcHAKICAgIGNvbW1hbmRzOgogICAgICAtIG1ha2UgdGVzdC1wZ3NxbAogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCwgdGFnLCBwdWxsX3JlcXVlc3QgXQoKICBzdGF0aWM6CiAgICBpbWFnZToga2FyYWxhYmUveGdvLWxhdGVzdDpsYXRlc3QKICAgIHB1bGw6IHRydWUKICAgIGVudmlyb25tZW50OgogICAgICBUQUdTOiBiaW5kYXRhIHNxbGl0ZQogICAgICBHT1BBVEg6IC9zcnYvYXBwCiAgICBjb21tYW5kczoKICAgICAgLSBtYWtlIHJlbGVhc2UKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2gsIHRhZywgcHVsbF9yZXF1ZXN0IF0KCiAgY292ZXJhZ2U6CiAgICBpbWFnZTogcGx1Z2lucy9jb3ZlcmFnZQogICAgc2VydmVyOiBodHRwczovL2NvdmVyYWdlLmdpdGVhLmlvCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoLCB0YWcsIHB1bGxfcmVxdWVzdCBdCgogIGRvY2tlcjoKICAgIGltYWdlOiBwbHVnaW5zL2RvY2tlcgogICAgcmVwbzogZ2l0ZWEvZ2l0ZWEKICAgIHRhZ3M6IFsgJyR7RFJPTkVfVEFHIyN2fScgXQogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgdGFnIF0KICAgICAgYnJhbmNoOiBbIHJlZnMvdGFncy8qIF0KCiAgZG9ja2VyOgogICAgaW1hZ2U6IHBsdWdpbnMvZG9ja2VyCiAgICByZXBvOiBnaXRlYS9naXRlYQogICAgdGFnczogWyAnJHtEUk9ORV9CUkFOQ0gjI3JlbGVhc2Uvdn0nIF0KICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2ggXQogICAgICBicmFuY2g6IFsgcmVsZWFzZS8qIF0KCiAgZG9ja2VyOgogICAgaW1hZ2U6IHBsdWdpbnMvZG9ja2VyCiAgICByZXBvOiBnaXRlYS9naXRlYQogICAgdGFnczogWyAnbGF0ZXN0JyBdCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoIF0KICAgICAgYnJhbmNoOiBbIG1hc3RlciBdCgogIHJlbGVhc2U6CiAgICBpbWFnZTogcGx1Z2lucy9zMwogICAgcGF0aF9zdHlsZTogdHJ1ZQogICAgc3RyaXBfcHJlZml4OiBkaXN0L3JlbGVhc2UvCiAgICBzb3VyY2U6IGRpc3QvcmVsZWFzZS8qCiAgICB0YXJnZXQ6IC9naXRlYS8ke0RST05FX1RBRyMjdn0KICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHRhZyBdCiAgICAgIGJyYW5jaDogWyByZWZzL3RhZ3MvKiBdCgogIHJlbGVhc2U6CiAgICBpbWFnZTogcGx1Z2lucy9zMwogICAgcGF0aF9zdHlsZTogdHJ1ZQogICAgc3RyaXBfcHJlZml4OiBkaXN0L3JlbGVhc2UvCiAgICBzb3VyY2U6IGRpc3QvcmVsZWFzZS8qCiAgICB0YXJnZXQ6IC9naXRlYS8ke0RST05FX0JSQU5DSCMjcmVsZWFzZS92fQogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgcHVzaCBdCiAgICAgIGJyYW5jaDogWyByZWxlYXNlLyogXQoKICByZWxlYXNlOgogICAgaW1hZ2U6IHBsdWdpbnMvczMKICAgIHBhdGhfc3R5bGU6IHRydWUKICAgIHN0cmlwX3ByZWZpeDogZGlzdC9yZWxlYXNlLwogICAgc291cmNlOiBkaXN0L3JlbGVhc2UvKgogICAgdGFyZ2V0OiAvZ2l0ZWEvbWFzdGVyCiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoIF0KICAgICAgYnJhbmNoOiBbIG1hc3RlciBdCgogIGdpdGh1YjoKICAgIGltYWdlOiBwbHVnaW5zL2dpdGh1Yi1yZWxlYXNlCiAgICBmaWxlczoKICAgICAgLSBkaXN0L3JlbGVhc2UvKgogICAgd2hlbjoKICAgICAgZXZlbnQ6IFsgdGFnIF0KICAgICAgYnJhbmNoOiBbIHJlZnMvdGFncy8qIF0KCiAgZ2l0dGVyOgogICAgaW1hZ2U6IHBsdWdpbnMvZ2l0dGVyCgpzZXJ2aWNlczoKICBteXNxbDoKICAgIGltYWdlOiBteXNxbDo1LjcKICAgIGVudmlyb25tZW50OgogICAgICAtIE1ZU1FMX0RBVEFCQVNFPXRlc3QKICAgICAgLSBNWVNRTF9BTExPV19FTVBUWV9QQVNTV09SRD15ZXMKICAgIHdoZW46CiAgICAgIGV2ZW50OiBbIHB1c2gsIHRhZywgcHVsbF9yZXF1ZXN0IF0KCiAgcGdzcWw6CiAgICBpbWFnZTogcG9zdGdyZXM6OS41CiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBQT1NUR1JFU19EQj10ZXN0CiAgICB3aGVuOgogICAgICBldmVudDogWyBwdXNoLCB0YWcsIHB1bGxfcmVxdWVzdCBdCg.hp6IsxbFIQOaxJdmGv32Vf34-Nra3KqVIWzH52W687I

View File

@@ -1,11 +1,11 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf
[*.go]
indent_style = tab
@@ -15,17 +15,13 @@ indent_size = 8
indent_style = tab
indent_size = 4
[*.less]
[*.{less,yml}]
indent_style = space
indent_size = 4
[*.{yml,json}]
indent_style = space
indent_size = 2
[*.js]
indent_style = space
indent_size = 2
indent_size = 4
[Makefile]
indent_style = tab

View File

@@ -1 +0,0 @@
/web_src/js/semanticDropdown.js

View File

@@ -1,53 +0,0 @@
root: true
extends:
- eslint-config-airbnb-base
- eslint:recommended
parserOptions:
ecmaVersion: 2020
env:
browser: true
es6: true
jquery: true
node: true
globals:
__webpack_public_path__: true
Clipboard: false
CodeMirror: false
Dropzone: false
emojify: false
hljs: false
SimpleMDE: false
u2fApi: false
Vue: false
rules:
arrow-body-style: [0]
camelcase: [0]
comma-dangle: [2, only-multiline]
consistent-return: [0]
default-case: [0]
func-names: [0]
import/extensions: [0]
max-len: [0]
newline-per-chained-call: [0]
no-alert: [0]
no-continue: [0]
no-mixed-operators: [0]
no-multi-assign: [0]
no-new: [0]
no-param-reassign: [0]
no-plusplus: [0]
no-restricted-syntax: [0]
no-shadow: [0]
no-unused-vars: [2, {args: all, argsIgnorePattern: ^_, varsIgnorePattern: ^_, ignoreRestSiblings: true}]
no-use-before-define: [0]
no-var: [2]
one-var-declaration-per-line: [0]
one-var: [0]
prefer-const: [2, {destructuring: all}]
prefer-destructuring: [0]
radix: [2, as-needed]

3
.gitattributes vendored
View File

@@ -1,7 +1,6 @@
* text=auto eol=lf
conf/* linguist-vendored
docker/* linguist-vendored
options/* linguist-vendored
public/* linguist-vendored
scripts/* linguist-vendored
templates/* linguist-vendored
templates/* linguist-vendored

1
.github/FUNDING.yml vendored
View File

@@ -1 +0,0 @@
open_collective: gitea

View File

@@ -1,13 +1,9 @@
<!-- NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue -->
1. Please speak English, this is the language all of us can speak and write.
2. Please ask questions or configuration/deploy problems on our Gitter channel: https://gitter.im/go-gitea/gitea
3. Please take a moment to search check that your issue doesn't already exist.
4. Please give all relevant information below for bug reports, because incomplete details will be handled as an invalid report.
<!--
1. Please speak English, this is the language all maintainers can speak and write.
2. Please ask questions or configuration/deploy problems on our Discord
server (https://discord.gg/gitea) or forum (https://discourse.gitea.io).
3. Please take a moment to check that your issue doesn't already exist.
4. Please give all relevant information below for bug reports, because
incomplete details will be handled as an invalid report.
-->
**You MUST delete the content above including this line before posting, otherwise your issue will be invalid.**
- Gitea version (or commit ref):
- Git version:
@@ -15,7 +11,6 @@
- Database (use `[x]`):
- [ ] PostgreSQL
- [ ] MySQL
- [ ] MSSQL
- [ ] SQLite
- Can you reproduce the bug at https://try.gitea.io:
- [ ] Yes (provide example URL)
@@ -30,4 +25,4 @@
## Screenshots
<!-- **If this issue involves the Web Interface, please include a screenshot** -->
**If this issue involves the Web Interface, please include a screenshot**

53
.github/stale.yml vendored
View File

@@ -1,53 +0,0 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 60
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: 14
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
- status/blocked
- kind/security
- lgtm/done
- reviewed/confirmed
- priority/critical
- kind/proposal
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: false
# Label to use when marking as stale
staleLabel: stale
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs during the next 2 weeks. Thank you
for your contributions.
# Comment to post when closing a stale Issue or Pull Request.
closeComment: >
This issue has been automatically closed because of inactivity.
You can re-open it if needed.
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 1
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
pulls:
daysUntilStale: 60
daysUntilClose: 60
markComment: >
This pull request has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs during the next 2 months. Thank you
for your contributions.
closeComment: >
This pull request has been automatically closed because of inactivity.
You can re-open it if needed.

37
.gitignore vendored
View File

@@ -10,10 +10,6 @@ _test
# IntelliJ
.idea
# MS VSCode
.vscode
__debug_bin
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
@@ -30,8 +26,7 @@ _testmain.go
*.test
*.prof
*coverage.out
coverage.all
coverage.out
/modules/options/bindata.go
/modules/public/bindata.go
@@ -41,8 +36,6 @@ coverage.all
*.log
/gitea
/debug
/integrations.test
/bin
/dist
@@ -51,31 +44,3 @@ coverage.all
/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/mysql.ini
/integrations/mysql8.ini
/integrations/pgsql.ini
/integrations/mssql.ini
/node_modules
/yarn.lock
/public/js
/public/css
# Snapcraft
snap/.snapcraft/
parts/
stage/
prime/
*.snap
*.snap-build
*_source.tar.bz2
.DS_Store

View File

@@ -1,97 +0,0 @@
linters:
enable:
- gosimple
- deadcode
- typecheck
- govet
- errcheck
- staticcheck
- unused
- structcheck
- varcheck
- golint
- dupl
#- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
- gofmt
- misspell
- gocritic
enable-all: false
disable-all: true
fast: false
run:
timeout: 3m
linters-settings:
gocritic:
disabled-checks:
- ifElseChain
- singleCaseSwitch # Every time this occured in the code, there was no other way.
issues:
exclude-rules:
# Exclude some linters from running on tests files.
- path: _test\.go
linters:
- gocyclo
- errcheck
- dupl
- gosec
- unparam
- staticcheck
- path: models/migrations/v
linters:
- gocyclo
- errcheck
- dupl
- gosec
- linters:
- dupl
text: "webhook"
- linters:
- gocritic
text: "`ID' should not be capitalized"
- path: modules/templates/helper.go
linters:
- gocritic
- linters:
- unused
- deadcode
text: "swagger"
- path: contrib/pr/checkout.go
linters:
- errcheck
- path: models/issue.go
linters:
- errcheck
- path: models/migrations/
linters:
- errcheck
- path: modules/log/
linters:
- errcheck
- path: routers/routes/routes.go
linters:
- dupl
- path: routers/api/v1/repo/issue_subscription.go
linters:
- dupl
- path: routers/repo/view.go
linters:
- dupl
- path: models/migrations/
linters:
- unused
- linters:
- staticcheck
text: "argument x is overwritten before first use"
- path: modules/httplib/httplib.go
linters:
- staticcheck
# Enabling this would require refactoring the methods and how they are called.
- path: models/issue_comment_list.go
linters:
- dupl
- linters:
- misspell
text: '`Unknwon` is a misspelling of `Unknown`'

View File

@@ -1,5 +0,0 @@
/vendor
/public/vendor
/modules/options/bindata.go
/modules/public/bindata.go
/modules/templates/bindata.go

3
.lgtm
View File

@@ -1,3 +1,2 @@
pattern = "(?)LGTM"
self_approval_off = true
self_approval_off = false
ignore_maintainers_file = true

1
.npmrc
View File

@@ -1 +0,0 @@
save-exact=true

View File

@@ -1,25 +0,0 @@
ignoreGeneratedHeader = false
severity = "warning"
confidence = 0.8
errorCode = 1
warningCode = 1
[rule.blank-imports]
[rule.context-as-argument]
[rule.context-keys-type]
[rule.dot-imports]
[rule.error-return]
[rule.error-strings]
[rule.error-naming]
[rule.exported]
[rule.if-return]
[rule.increment-decrement]
[rule.var-naming]
[rule.var-declaration]
[rule.package-comments]
[rule.range]
[rule.receiver-naming]
[rule.time-naming]
[rule.unexported-return]
[rule.indent-error-flow]
[rule.errorf]

View File

@@ -1,11 +0,0 @@
extends: stylelint-config-standard
rules:
block-closing-brace-empty-line-before: null
color-hex-length: null
comment-empty-line-before: null
declaration-empty-line-before: null
indentation: 4
no-descending-specificity: null
rule-empty-line-before: null
selector-pseudo-element-colon-notation: null

View File

@@ -1,51 +0,0 @@
# GNU makefile proxy script for BSD make
# Written and maintained by Mahmoud Al-Qudsi <mqudsi@neosmart.net>
# Copyright NeoSmart Technologies <https://neosmart.net/> 2014-2018
# Obtain updates from <https://github.com/neosmart/gmake-proxy>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
JARG =
GMAKE = "gmake"
#When gmake is called from another make instance, -w is automatically added
#which causes extraneous messages about directory changes to be emitted.
#--no-print-directory silences these messages.
GARGS = "--no-print-directory"
.if "$(.MAKE.JOBS)" != ""
JARG = -j$(.MAKE.JOBS)
.endif
#by default bmake will cd into ./obj first
.OBJDIR: ./
.PHONY: FRC
$(.TARGETS): FRC
$(GMAKE) $(GARGS) $(.TARGETS:S,.DONE,,) $(JARG)
.DONE .DEFAULT: .SILENT
$(GMAKE) $(GARGS) $(.TARGETS:S,.DONE,,) $(JARG)
.ERROR: .SILENT
if ! which $(GMAKE) > /dev/null; then \
echo "GNU Make is required!"; \
fi

File diff suppressed because it is too large Load Diff

View File

@@ -1,148 +1,48 @@
# Contribution Guidelines
## Table of Contents
- [Contribution Guidelines](#contribution-guidelines)
- [Introduction](#introduction)
- [Bug reports](#bug-reports)
- [Discuss your design](#discuss-your-design)
- [Testing redux](#testing-redux)
- [Vendoring](#vendoring)
- [Translation](#translation)
- [Code review](#code-review)
- [Styleguide](#styleguide)
- [Design guideline](#design-guideline)
- [API v1](#api-v1)
- [Developer Certificate of Origin (DCO)](#developer-certificate-of-origin-dco)
- [Release Cycle](#release-cycle)
- [Maintainers](#maintainers)
- [Owners](#owners)
- [Versions](#versions)
- [Releasing Gitea](#releasing-gitea)
- [Copyright](#copyright)
## Introduction
This document explains how to contribute changes to the Gitea project.
It assumes you have followed the
[installation instructions](https://docs.gitea.io/en-us/).
Sensitive security-related issues should be reported to
[security@gitea.io](mailto:security@gitea.io).
For configuring IDE or code editor to develop Gitea see [IDE and code editor configuration](contrib/ide/)
This document explains how to contribute changes to the Gitea project. It assumes you have followed the [installation instructions](https://docs.gitea.io/en-us/). Sensitive security-related issues should be reported to [security@gitea.io](mailto:security@gitea.io).
## Bug reports
Please search the issues on the issue tracker with a variety of keywords
to ensure your bug is not already reported.
Please search the issues on the issue tracker with a variety of keywords to ensure your bug is not already reported.
If unique, [open an issue](https://github.com/go-gitea/gitea/issues/new)
and answer the questions so we can understand and reproduce the
problematic behavior.
If unique, [open an issue](https://github.com/go-gitea/gitea/issues/new) and answer the questions so we can understand and reproduce the problematic behavior.
To show us that the issue you are having is in Gitea itself, please
write clear, concise instructions so we can reproduce the behavior—
even if it seems obvious. The more detailed and specific you are,
the faster we can fix the issue. Check out [How to Report Bugs
Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html).
To show us that the issue you are having is in Gitea itself, please write clear, concise instructions so we can reproduce the behavior (even if it seems obvious). The more detailed and specific you are, the faster we can fix the issue. Check out [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html).
Please be kind, remember that Gitea comes at no cost to you, and you're
getting free help.
Please be kind, remember that Gitea comes at no cost to you, and you're getting free help.
## Discuss your design
The project welcomes submissions. If you want to change or add something,
please let everyone know what you're working on—[file an issue](https://github.com/go-gitea/gitea/issues/new)!
Significant changes must go through the change proposal process
before they can be accepted. To create a proposal, file an issue with
your proposed changes documented, and make sure to note in the title
of the issue that it is a proposal.
The project welcomes submissions but please let everyone know what you're working on if you want to change or add something to the Gitea repositories.
This process gives everyone a chance to validate the design, helps
prevent duplication of effort, and ensures that the idea fits inside
the goals for the project and tools. It also checks that the design is
sound before code is written; the code review tool is not the place for
high-level discussions.
Before starting to write something new for the Gitea project, please [file an issue](https://github.com/go-gitea/gitea/issues/new). Significant changes must go through the [change proposal process](https://github.com/go-gitea/proposals) before they can be accepted.
This process gives everyone a chance to validate the design, helps prevent duplication of effort, and ensures that the idea fits inside the goals for the project and tools. It also checks that the design is sound before code is written; the code review tool is not the place for high-level discussions.
## Testing redux
Before submitting a pull request, run all the tests for the whole tree
to make sure your changes don't cause regression elsewhere.
Here's how to run the test suite:
- Install the correct version of the drone-cli package. As of this
writing, the correct drone-cli version is
[1.2.0](https://docs.drone.io/cli/install/).
- Ensure you have enough free disk space. You will need at least
15-20 Gb of free disk space to hold all of the containers drone
creates (a default AWS or GCE disk size won't work -- see
[#6243](https://github.com/go-gitea/gitea/issues/6243)).
- Change into the base directory of your copy of the gitea repository,
and run `drone exec --event pull_request`.
- At the moment `drone exec` doesn't support the Docker Toolbox on Windows 10
(see [drone-cli#135](https://github.com/drone/drone-cli/issues/135))
The drone version, command line, and disk requirements do change over
time (see [#4053](https://github.com/go-gitea/gitea/issues/4053) and
[#6243](https://github.com/go-gitea/gitea/issues/6243)); if you
discover any issues, please feel free to send us a pull request to
update these instructions.
Before sending code out for review, run all the tests for the whole tree to make sure the changes don't break other usage and keep the compatibility on upgrade. To make sure you are running the test suite exactly like we do, you should install the CLI for [Drone CI](https://github.com/drone/drone), as we are using the server for continous testing, following [these instructions](http://readme.drone.io/usage/getting-started-cli). After that you can simply call `drone exec` within your working directory and it will try to run the test suite locally.
## Vendoring
We keep a cached copy of dependencies within the `vendor/` directory,
managing updates via [Modules](https://golang.org/cmd/go/#hdr-Module_maintenance).
We keep a cached copy of dependencies within the `vendor/` directory, managing updates via [govendor](http://github.com/kardianos/govendor).
Pull requests should only include `vendor/` updates if they are part of
the same change, be it a bugfix or a feature addition.
Pull requests should only include `vendor/` updates if they are part of the same change, be it a bugfix or a feature addition.
The `vendor/` update needs to be justified as part of the PR description,
and must be verified by the reviewers and/or merger to always reference
an existing upstream commit.
You can find more information on how to get started with it on the [Modules Wiki](https://github.com/golang/go/wiki/Modules).
## Translation
We do all translation work inside [Crowdin](https://crowdin.com/project/gitea).
The only translation that is maintained in this git repository is
[`en_US.ini`](https://github.com/go-gitea/gitea/blob/master/options/locale/locale_en-US.ini)
and is synced regularly to Crowdin. Once a translation has reached
A SATISFACTORY PERCENTAGE it will be synced back into this repo and
included in the next released version.
## Building Gitea
Generally, the go build tools are installed as-needed in the `Makefile`.
An exception are the tools to build the CSS, JS and images.
- To build CSS and JS: Install [Node.js](https://nodejs.org/en/download/package-manager) at version 10.0 or above
with `npm` and then run `npm install`, `make css` and `make js`.
- To build Images: ImageMagick, inkscape and zopflipng binaries must be
available in your `PATH` to run `make generate-images`.
For more details on how to generate files, build and test Gitea, see the [hacking instructions](https://docs.gitea.io/en-us/hacking-on-gitea/)
The `vendor/` update needs to be justified as part of the PR description, and must be verified by the reviewers and/or merger to always reference an existing upstream commit.
## Code review
Changes to Gitea must be reviewed before they are acceptedno matter who
makes the change, even if they are an owner or a maintainer. We use GitHub's
pull request workflow to do that. And, we also use [LGTM](http://lgtm.co)
to ensure every PR is reviewed by at least 2 maintainers.
Changes to Gitea must be reviewed before they are accepted, no matter who makes the change even if it is an owner or a maintainer. We use GitHub's pull request workflow to do that and we also use [LGTM](http://lgtm.co) to ensure every PR is reviewed by at least 2 maintainers.
Please try to make your pull request easy to review for us. And, please read
the *[How to get faster PR reviews](https://github.com/kubernetes/community/blob/261cb0fd089b64002c91e8eddceebf032462ccd6/contributors/guide/pull-requests.md#best-practices-for-faster-reviews)* guide;
it has lots of useful tips for any project you may want to contribute.
Some of the key points:
Please try to make your pull request easy to review for us. Please read the "[How to get faster PR reviews](https://github.com/kubernetes/community/blob/master/contributors/devel/faster_reviews.md)" guide, it has lots of useful tips for any project you may want to contribute. Some of the key points:
* Make small pull requests. The smaller, the faster to review and the
more likely it will be merged soon.
* Don't make changes unrelated to your PR. Maybe there are typos on
some comments, maybe refactoring would be welcome on a function... but
if that is not related to your PR, please make *another* PR for that.
* Split big pull requests into multiple small ones. An incremental change
will be faster to review than a huge PR.
* Make small pull requests. The smaller, the faster to review and the more likely it will be merged soon.
* Don't make changes unrelated to your PR. Maybe there are typos on some comments, maybe refactoring would be welcome on a function... but if that is not related to your PR, please make *another* PR for that.
* Split big pull requests into multiple small ones. An incremental change will be faster to review than a huge PR.
## Styleguide
@@ -163,202 +63,57 @@ import (
)
```
## Design guideline
## Sign your work
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
- **models:** Contains the data structures used by xorm to construct database tables. It also contains supporting functions to query and update the database. Dependecies 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.
- **modules:** Different modules to handle specific functionality in Gitea.
- **public:** Frontend files (javascript, images, css, etc.)
- **routers:** Handling of server requests. As it uses other Gitea packages to serve the request, other packages (models, modules or services) shall not depend on routers
- **services:** Support functions for common routing operations. Uses models and modules to handle the request.
- **templates:** Golang templates for generating the html output.
- **vendor:** External code that Gitea depends on.
## API v1
The API is documented by [swagger](http://try.gitea.io/api/swagger) and is based on [GitHub API v3](https://developer.github.com/v3/).
Thus, Gitea´s API should use the same endpoints and fields as GitHub´s API as far as possible, unless there are good reasons to deviate.
If Gitea provides functionality that GitHub does not, a new endpoint can be created.
If information is provided by Gitea that is not provided by the GitHub API, a new field can be used that doesn't collide with any GitHub fields.
Updating an existing API should not remove existing fields unless there is a really good reason to do so.
The same applies to status responses. If you notice a problem, feel free to leave a comment in the code for future refactoring to APIv2 (which is currently not planned).
All expected results (errors, success, fail messages) should be documented
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L319-L327)).
All JSON input types must be defined as a struct in `models/structs/`
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/modules/structs/issue.go#L76-L91))
and referenced in
[routers/api/v1/swagger/options.go](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/swagger/options.go).
They can then be used like the following:
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L318)).
All JSON responses must be defined as a struct in `models/structs/`
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/modules/structs/issue.go#L36-L68))
and referenced in its category in `routers/api/v1/swagger/`
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/swagger/issue.go#L11-L16))
They can be used like the following:
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L277-L279))
In general, HTTP methods are chosen as follows:
* **GET** endpoints return requested object and status **OK (200)**
* **DELETE** endpoints return status **No Content (204)**
* **POST** endpoints return status **Created (201)**, used to **create** new objects (e.g. a User)
* **PUT** endpoints return status **No Content (204)**, used to **add/assign** existing Obejcts (e.g. User) to something (e.g. Org-Team)
* **PATCH** endpoints return changed object and status **OK (200)**, used to **edit/change** an existing object
An endpoint which changes/edits an object expects all fields to be optional (except ones to identify the object, which are required).
## Developer Certificate of Origin (DCO)
We consider the act of contributing to the code by submitting a Pull
Request as the "Sign off" or agreement to the certifications and terms
of the [DCO](DCO) and [MIT license](LICENSE). No further action is required.
Additionally you could add a line at the end of your commit message.
The sign-off is a simple line at the end of the explanation for the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: If you can certify [DCO](DCO), then you just add a line to every git commit message:
```
Signed-off-by: Joe Smith <joe.smith@email.com>
```
If you set your `user.name` and `user.email` git configs, you can add the
line to the end of your commit automatically with `git commit -s`.
We assume in good faith that the information you provide is legally binding.
Please use your real name, we really dislike pseudonyms or anonymous contributions. We are in the open-source world without secrets. If you set your `user.name` and `user.email` git configs, you can sign your commit automatically with `git commit -s`.
## Release Cycle
We adopted a release schedule to streamline the process of working
on, finishing, and issuing releases. The overall goal is to make a
minor release every two months, which breaks down into one month of
general development followed by one month of testing and polishing
known as the release freeze. All the feature pull requests should be
merged in the first month of one release period. And, during the frozen
period, a corresponding release branch is open for fixes backported from
master. Release candidates are made during this period for user testing to
obtain a final version that is maintained in this branch. A release is
maintained by issuing patch releases to only correct critical problems
such as crashes or security issues.
We adopted a release schedule to streamline the process of working on, finishing, and issuing releases. The overall goal is to make a major release every two months, which breaks down into one month of general development followed by one month of testing and polishing known as the release freeze. A release is maintained by issuing minor releases to only correct critical problems such as crashes or security issues. All the feature pull requests should be merged in the first month of one release period.
Major release cycles are bimonthly. They always begin on the 25th and end on
the 24th (i.e., the 25th of December to February 24th).
During a development cycle, we may also publish any necessary minor releases
for the previous version. For example, if the latest, published release is
v1.2, then minor changes for the previous release—e.g., v1.1.0 -> v1.1.1—are
still possible.
The current release cycle is aligned to start on December 25 to February 24, next is February 25 to April 24, and etc. On this cycle, we also maybe publish the previous release minor version. For example, the current release version is v1.1, but we maybe also publish v1.0.2. When we publish v1.2, then we will stop publish v1.0.3.
## Maintainers
To make sure every PR is checked, we have [team
maintainers](MAINTAINERS). Every PR **MUST** be reviewed by at least
two maintainers (or owners) before it can get merged. A maintainer
should be a contributor of Gitea (or Gogs) and contributed at least
4 accepted PRs. A contributor should apply as a maintainer in the
[Discord](https://discord.gg/NsatcWJ) #develop channel. The owners
or the team maintainers may invite the contributor. A maintainer
should spend some time on code reviews. If a maintainer has no
time to do that, they should apply to leave the maintainers team
and we will give them the honor of being a member of the [advisors
team](https://github.com/orgs/go-gitea/teams/advisors). Of course, if
an advisor has time to code review, we will gladly welcome them back
to the maintainers team. If a maintainer is inactive for more than 3
months and forgets to leave the maintainers team, the owners may move
him or her from the maintainers team to the advisors team.
For security reasons, Maintainers should use 2FA for their accounts and
if possible provide gpg signed commits.
https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/
https://help.github.com/articles/signing-commits-with-gpg/
To make sure every PR is checked, we have [team maintainers](https://github.com/orgs/go-gitea/teams/maintainers). Every PR **MUST** be reviewed by at least two maintainers (or owners) before it can get merged. A maintainer should be a contributor of Gitea (or Gogs) and contributed at least 4 accepted PRs. A contributor should apply as a maintainer in the [Gitter develop channel](https://gitter.im/go-gitea/develop). The owners or the team maintainers may invite the contributor. A maintainer should spend some time on code reviews. If a maintainer has no time to do that, they should apply to leave the maintainers team and we will give them the honor of being a member of the [advisors team](https://github.com/orgs/go-gitea/teams/advisors). Of course, if an advisor has time to code review, we will gladly welcome them back to the maintainers team. If a maintainer is inactive for more than 3 months and forgets to leave the maintainers team, the owners may move him or her from the maintainers team to the advisors team.
## Owners
Since Gitea is a pure community organization without any company support,
to keep the development healthy we will elect three owners every year. All
contributors may vote to elect up to three candidates, one of which will
be the main owner, and the other two the assistant owners. When the new
owners have been elected, the old owners will give up ownership to the
newly elected owners. If an owner is unable to do so, the other owners
will assist in ceding ownership to the newly elected owners.
For security reasons, Owners or any account with write access (like a bot)
must use 2FA.
https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/
Since Gitea is a pure community organization without any company support, to keep the development healthy we will elect three owners every year. All contributors may vote to elect up to three candidates, one of which will be the main owner, and the other two the assistant owners. When the new owners have been elected, the old owners will give up ownership to the newly elected owners. If an owner is unable to do so, the other owners will assist in ceding ownership to the newly elected owners.
After the election, the new owners should proactively agree
with our [CONTRIBUTING](CONTRIBUTING.md) requirements in the
[Discord](https://discord.gg/NsatcWJ) #general channel. Below are the
words to speak:
After the election, the new owners should proactively agree with our [CONTRIBUTING](CONTRIBUTING.md) requirements on the [Gitter main channel](https://gitter.im/go-gitea/gitea). Below are the words to speak:
```
I'm honored to having been elected an owner of Gitea, I agree with
[CONTRIBUTING](CONTRIBUTING.md). I will spend part of my time on Gitea
and lead the development of Gitea.
I'm honored to having been elected an owner of Gitea, I agree with [CONTRIBUTING](CONTRIBUTING.md). I will spend part of my time on Gitea and lead the development of Gitea.
```
To honor the past owners, here's the history of the owners and the time
they served:
To honor the past owners, here's the history of the owners and the time they served:
* 2016-11-04 ~ 2017-12-31
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
* [Thomas Boerger](https://github.com/tboerger) <thomas@webhippie.de>
* [Kim Carlbäcker](https://github.com/bkcsoft) <kim.carlbacker@gmail.com>
* 2018-01-01 ~ 2018-12-31
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
* [Lauris Bukšis-Haberkorns](https://github.com/lafriks) <lauris@nix.lv>
* [Kim Carlbäcker](https://github.com/bkcsoft) <kim.carlbacker@gmail.com>
* 2019-01-01 ~ 2019-12-31
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
* [Lauris Bukšis-Haberkorns](https://github.com/lafriks) <lauris@nix.lv>
* [Matti Ranta](https://github.com/techknowlogick) <techknowlogick@gitea.io>
* 2020-01-01 ~ 2020-12-31
* [Lunny Xiao](https://gitea.com/lunny) <xiaolunwen@gmail.com>
* [Lauris Bukšis-Haberkorns](https://gitea.com/lafriks) <lauris@nix.lv>
* [Matti Ranta](https://gitea.com/techknowlogick) <techknowlogick@gitea.io>
## Versions
Gitea has the `master` branch as a tip branch and has version branches
such as `release/v0.9`. `release/v0.9` is a release branch and we will
tag `v0.9.0` for binary download. If `v0.9.0` has bugs, we will accept
pull requests on the `release/v0.9` branch and publish a `v0.9.1` tag,
after bringing the bug fix also to the master branch.
Gitea has the `master` branch as a tip branch and has version branches such as `v0.9`. `v0.9` is a release branch and we will tag `v0.9.0` for binary download. If `v0.9.0` has bugs, we will accept pull requests on the `v0.9` branch and publish a `v0.9.1` tag, after bringing the bug fix also to the master branch.
Since the `master` branch is a tip version, if you wish to use Gitea
in production, please download the latest release tag version. All the
branches will be protected via GitHub, all the PRs to every branch must
be reviewed by two maintainers and must pass the automatic tests.
## Releasing Gitea
* Let $vmaj, $vmin and $vpat be Major, Minor and Patch version numbers, $vpat should be rc1, rc2, 0, 1, ...... $vmaj.$vmin will be kept the same as milestones on github or gitea in future.
* Before releasing, confirm all the version's milestone issues or PRs has been resolved. Then discuss the release on discord channel #maintainers and get agreed with almost all the owners and mergers. Or you can declare the version and if nobody against in about serval hours.
* If this is a big version first you have to create PR for changelog on branch `master` with PRs with label `changelog` and after it has been merged do following steps:
* Create `-dev` tag as `git tag -s -F release.notes v$vmaj.$vmin.0-dev` and push the tag as `git push origin v$vmaj.$vmin.0-dev`.
* When CI has finished building tag then you have to create a new branch named `release/v$vmaj.$vmin`
* If it is bugfix version create PR for changelog on branch `release/v$vmaj.$vmin` and wait till it is reviewed and merged.
* Add a tag as `git tag -s -F release.notes v$vmaj.$vmin.$`, release.notes file could be a temporary file to only include the changelog this version which you added to `CHANGELOG.md`.
* And then push the tag as `git push origin v$vmaj.$vmin.$`. Drone CI will automatically created a release and upload all the compiled binary. (But currently it didn't add the release notes automatically. Maybe we should fix that.)
* If needed send PR for changelog on branch `master`.
* Send PR to [blog repository](https://gitea.com/gitea/blog) announcing the release.
Since the `master` branch is a tip version, if you wish to use Gitea in production, please download the latest release tag version. All the branches will be protected via GitHub, all the PRs to every branch must be reviewed by two maintainers and must pass the automatic tests.
## Copyright
Code that you contribute should use the standard copyright header:
```
// Copyright 2019 The Gitea Authors. All rights reserved.
// 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.
```
Files in the repository contain copyright from the year they are added
to the year they are last changed. If the copyright author is changed,
just paste the header below the old one.
Files in the repository contain copyright from the year they are added to the year they are last changed. If the copyright author is changed, just paste the header below the old one.

View File

@@ -1,45 +1,23 @@
###################################
#Build stage
FROM golang:1.13-alpine3.11 AS build-env
ARG GOPROXY
ENV GOPROXY ${GOPROXY:-direct}
ARG GITEA_VERSION
ARG TAGS="sqlite sqlite_unlock_notify"
ENV TAGS "bindata $TAGS"
#Build deps
RUN apk --no-cache add build-base git nodejs npm
#Setup repo
COPY . ${GOPATH}/src/code.gitea.io/gitea
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
#Checkout version if set
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
&& make clean build
FROM alpine:3.11
LABEL maintainer="maintainers@gitea.io"
FROM alpine:3.4
MAINTAINER Thomas Boerger <thomas@webhippie.de>
EXPOSE 22 3000
RUN apk --no-cache add \
bash \
RUN apk update && \
apk add \
su-exec \
ca-certificates \
curl \
gettext \
sqlite \
bash \
git \
linux-pam \
openssh \
s6 \
sqlite \
su-exec \
tzdata
RUN addgroup \
curl \
openssh \
tzdata && \
rm -rf \
/var/cache/apk/* && \
addgroup \
-S -g 1000 \
git && \
adduser \
@@ -49,16 +27,16 @@ RUN addgroup \
-u 1000 \
-G git \
git && \
echo "git:$(dd if=/dev/urandom bs=24 count=1 status=none | base64)" | chpasswd
echo "git:$(date +%s | sha256sum | base64 | head -c 32)" | chpasswd
ENV USER git
ENV GITEA_CUSTOM /data/gitea
ENV GODEBUG=netdns=go
VOLUME ["/data"]
ENTRYPOINT ["/usr/bin/entrypoint"]
CMD ["/bin/s6-svscan", "/etc/s6"]
COPY docker/root /
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
RUN ln -s /app/gitea/gitea /usr/local/bin/gitea
COPY docker /
COPY gitea /app/gitea/gitea

42
Dockerfile.rpi Normal file
View File

@@ -0,0 +1,42 @@
FROM hypriot/rpi-alpine-scratch:v3.4
MAINTAINER Thomas Boerger <thomas@webhippie.de>
EXPOSE 22 3000
RUN apk update && \
apk add \
su-exec \
ca-certificates \
sqlite \
bash \
git \
linux-pam \
s6 \
curl \
openssh \
tzdata && \
rm -rf \
/var/cache/apk/* && \
addgroup \
-S -g 1000 \
git && \
adduser \
-S -H -D \
-h /data/git \
-s /bin/bash \
-u 1000 \
-G git \
git && \
echo "git:$(date +%s | sha256sum | base64 | head -c 32)" | chpasswd
ENV USER git
ENV GITEA_CUSTOM /data/gitea
ENV GODEBUG=netdns=go
VOLUME ["/data"]
ENTRYPOINT ["/usr/bin/entrypoint"]
CMD ["/bin/s6-svscan", "/etc/s6"]
COPY docker /
COPY gitea /app/gitea/gitea

View File

@@ -7,32 +7,8 @@ Kim Carlbäcker <kim.carlbacker@gmail.com> (@bkcsoft)
LefsFlare <nobody@nobody.tld> (@LefsFlarey)
Lunny Xiao <xiaolunwen@gmail.com> (@lunny)
Matthias Loibl <mail@matthiasloibl.com> (@metalmatze)
Morgan Bazalgette <the@howl.moe> (@thehowl)
Rachid Zarouali <nobody@nobody.tld> (@xinity)
Rémy Boulanouar <admin@dblk.org> (@DblK)
Sandro Santilli <strk@kbt.io> (@strk)
Thibault Meyer <meyer.thibault@gmail.com> (@0xbaadf00d)
Thomas Boerger <thomas@webhippie.de> (@tboerger)
Patrick G <geek1011@outlook.com> (@geek1011)
Antoine Girard <sapk@sapk.fr> (@sapk)
Lauris Bukšis-Haberkorns <lauris@nix.lv> (@lafriks)
Jonas Östanbäck <jonas.ostanback@gmail.com> (@cez81)
David Schneiderbauer <dschneiderbauer@gmail.com> (@daviian)
Peter Žeby <morlinest@gmail.com> (@morlinest)
Matti Ranta <techknowlogick@gitea.io> (@techknowlogick)
Jonas Franz <info@jonasfranz.software> (@jonasfranz)
Alexey Terentyev <axifnx@gmail.com> (@axifive)
Lanre Adelowo <yo@lanre.wtf> (@adelowo)
Konrad Langenberg <k@knt.li> (@kolaente)
He-Long Zhang <outman99@hotmail.com> (@BetaCat0)
Andrew Thornton <art27@cantab.net> (@zeripath)
John Olheiser <john.olheiser@gmail.com> (@jolheiser)
Richard Mahn <rich.mahn@unfoldingword.org> (@richmahn)
Mrsdizzie <info@mrsdizzie.com> (@mrsdizzie)
silverwind <me@silverwind.io> (@silverwind)
Gary Kim <gary@garykim.dev> (@gary-kim)
Guillermo Prandi <gitea.maint@mailfilter.com.ar> (@guillep2k)
Mura Li <typeless@ctli.io> (@typeless)
6543 <6543@obermui.de> (@6543)
jaqra <jaqra@hotmail.com> (@jaqra)
David Svantesson <davidsvantesson@gmail.com> (@davidsvantesson)

528
Makefile
View File

@@ -1,419 +1,107 @@
DIST := dist
IMPORT := code.gitea.io/gitea
export GO111MODULE=off
GO ?= go
SED_INPLACE := sed -i
SHASUM ?= shasum -a 256
export PATH := $($(GO) env GOPATH)/bin:$(PATH)
ifeq ($(OS), Windows_NT)
EXECUTABLE ?= gitea.exe
EXECUTABLE := gitea.exe
else
EXECUTABLE ?= gitea
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
SED_INPLACE := sed -i ''
endif
ifeq ($(UNAME_S),FreeBSD)
SED_INPLACE := sed -i ''
endif
EXECUTABLE := gitea
endif
GOFILES := $(shell find . -name "*.go" -type f ! -path "./vendor/*" ! -path "*/bindata.go")
GOFMT ?= gofmt -s
BINDATA := modules/{options,public,templates}/bindata.go
STYLESHEETS := $(wildcard public/less/index.less public/less/_*.less)
JAVASCRIPTS :=
GOFLAGS := -v
EXTRA_GOFLAGS ?=
LDFLAGS := -X "main.Version=$(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')" -X "main.Tags=$(TAGS)"
MAKE_VERSION := $(shell $(MAKE) -v | head -n 1)
PACKAGES ?= $(filter-out code.gitea.io/gitea/integrations,$(shell go list ./... | grep -v /vendor/))
SOURCES ?= $(shell find . -name "*.go" -type f)
TAGS ?=
ifneq ($(DRONE_TAG),)
VERSION ?= $(subst v,,$(DRONE_TAG))
GITEA_VERSION ?= $(VERSION)
else
ifneq ($(DRONE_BRANCH),)
VERSION ?= $(subst release/v,,$(DRONE_BRANCH))
else
VERSION ?= master
endif
GITEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
endif
LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
PACKAGES ?= $(filter-out code.gitea.io/gitea/integrations/migration-test,$(filter-out code.gitea.io/gitea/integrations,$(shell GO111MODULE=on $(GO) list -mod=vendor ./... | grep -v /vendor/)))
GO_SOURCES ?= $(shell find . -name "*.go" -type f)
JS_SOURCES ?= $(shell find web_src/js web_src/css -type f)
CSS_SOURCES ?= $(shell find web_src/less -type f)
JS_DEST := public/js/index.js
CSS_DEST := public/css/index.css
BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
JS_DEST_DIR := public/js
CSS_DEST_DIR := public/css
TAGS ?=
TMPDIR := $(shell mktemp -d 2>/dev/null || mktemp -d -t 'gitea-temp')
#To update swagger use: GO111MODULE=on go get -u github.com/go-swagger/go-swagger/cmd/swagger@v0.20.1
SWAGGER := GO111MODULE=on $(GO) run -mod=vendor github.com/go-swagger/go-swagger/cmd/swagger
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl}}/api/v1"|g
SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl}}/api/v1"|"basePath": "/api/v1"|g
SWAGGER_NEWLINE_COMMAND := -e '$$a\'
TEST_MYSQL_HOST ?= mysql:3306
TEST_MYSQL_DBNAME ?= testgitea
TEST_MYSQL_USERNAME ?= root
TEST_MYSQL_PASSWORD ?=
TEST_MYSQL8_HOST ?= mysql8:3306
TEST_MYSQL8_DBNAME ?= testgitea
TEST_MYSQL8_USERNAME ?= root
TEST_MYSQL8_PASSWORD ?=
TEST_PGSQL_HOST ?= pgsql:5432
TEST_PGSQL_DBNAME ?= testgitea
TEST_PGSQL_USERNAME ?= postgres
TEST_PGSQL_PASSWORD ?= postgres
TEST_MSSQL_HOST ?= mssql:1433
TEST_MSSQL_DBNAME ?= gitea
TEST_MSSQL_USERNAME ?= sa
TEST_MSSQL_PASSWORD ?= MwantsaSecurePassword1
# $(call strip-suffix,filename)
strip-suffix = $(firstword $(subst ., ,$(1)))
.PHONY: all
all: build
include docker/Makefile
.PHONY: help
help:
@echo "Make Routines:"
@echo " - \"\" equivalent to \"build\""
@echo " - build creates the entire project"
@echo " - clean delete integration files and build files but not css and js files"
@echo " - clean-all delete all generated files (integration test, build, css and js files)"
@echo " - css rebuild only css files"
@echo " - js rebuild only js files"
@echo " - generate run \"make css js\" and \"go generate\""
@echo " - fmt format the code"
@echo " - generate-swagger generate the swagger spec from code comments"
@echo " - swagger-validate check if the swagger spec is valide"
@echo " - revive run code linter revive"
@echo " - misspell check if a word is written wrong"
@echo " - vet examines Go source code and reports suspicious constructs"
@echo " - test run unit test"
@echo " - test-sqlite run integration test for sqlite"
.PHONY: go-check
go-check:
$(eval GO_VERSION := $(shell printf "%03d%03d%03d" $(shell go version | grep -Eo '[0-9]+\.?[0-9]+?\.?[0-9]?\s' | tr '.' ' ');))
@if [ "$(GO_VERSION)" -lt "001011000" ]; then \
echo "Gitea requires Go 1.11.0 or greater to build. You can get it at https://golang.org/dl/"; \
exit 1; \
fi
.PHONY: node-check
node-check:
$(eval NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v | grep -Eo '[0-9]+\.?[0-9]+?\.?[0-9]?' | tr '.' ' ');))
$(eval NPM_MISSING := $(shell hash npm > /dev/null 2>&1 || echo 1))
@if [ "$(NODE_VERSION)" -lt "010000000" -o "$(NPM_MISSING)" = "1" ]; then \
echo "Gitea requires Node.js 10.0.0 or greater and npm to build. You can get it at https://nodejs.org/en/download/"; \
exit 1; \
fi
.PHONY: clean-all
clean-all: clean
rm -rf $(JS_DEST_DIR) $(CSS_DEST_DIR)
.PHONY: clean
clean:
$(GO) clean -i ./...
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) \
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
go clean -i ./...
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA)
.PHONY: fmt
fmt:
$(GOFMT) -w $(GOFILES)
find . -name "*.go" -type f -not -path "./vendor/*" | xargs gofmt -s -w
.PHONY: vet
vet:
$(GO) vet $(PACKAGES)
go vet $(PACKAGES)
.PHONY: generate
generate: js css
GO111MODULE=on $(GO) generate -mod=vendor $(PACKAGES)
.PHONY: generate-swagger
generate-swagger:
$(SWAGGER) generate spec -o './$(SWAGGER_SPEC)'
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
$(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
.PHONY: swagger-check
swagger-check: generate-swagger
@diff=$$(git diff '$(SWAGGER_SPEC)'); \
if [ -n "$$diff" ]; then \
echo "Please run 'make generate-swagger' and commit the result:"; \
echo "$${diff}"; \
exit 1; \
fi;
.PHONY: swagger-validate
swagger-validate:
$(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)'
$(SWAGGER) validate './$(SWAGGER_SPEC)'
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
generate:
@hash go-bindata > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go get -u github.com/jteeuwen/go-bindata/...; \
fi
go generate $(PACKAGES)
.PHONY: errcheck
errcheck:
@hash errcheck > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/kisielk/errcheck; \
go get -u github.com/kisielk/errcheck; \
fi
errcheck $(PACKAGES)
.PHONY: lint
lint:
@echo 'make lint is depricated. Use "make revive" if you want to use the old lint tool, or "make golangci-lint" to run a complete code check.'
.PHONY: revive
revive:
@hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/mgechev/revive; \
@hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
go get -u github.com/golang/lint/golint; \
fi
revive -config .revive.toml -exclude=./vendor/... ./... || exit 1
for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done;
.PHONY: misspell-check
misspell-check:
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
fi
misspell -error -i unknwon,destory $(GOFILES)
.PHONY: misspell
misspell:
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
fi
misspell -w -i unknwon $(GOFILES)
.PHONY: fmt-check
fmt-check:
# get all go files and run go fmt on them
@diff=$$($(GOFMT) -d $(GOFILES)); \
if [ -n "$$diff" ]; then \
echo "Please run 'make fmt' and commit the result:"; \
echo "$${diff}"; \
exit 1; \
fi;
.PHONY: integrations
integrations: TAGS=bindata sqlite
integrations: build
go test code.gitea.io/gitea/integrations
.PHONY: test
test:
GO111MODULE=on $(GO) test -mod=vendor -tags='sqlite sqlite_unlock_notify' $(PACKAGES)
.PHONY: test\#%
test\#%:
GO111MODULE=on $(GO) test -mod=vendor -tags='sqlite sqlite_unlock_notify' -run $* $(PACKAGES)
.PHONY: coverage
coverage:
@hash gocovmerge > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/wadey/gocovmerge; \
fi
gocovmerge integration.coverage.out $(shell find . -type f -name "coverage.out") > coverage.all;\
.PHONY: unit-test-coverage
unit-test-coverage:
$(GO) test -tags='sqlite sqlite_unlock_notify' -cover -coverprofile coverage.out $(PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
.PHONY: vendor
vendor:
GO111MODULE=on $(GO) mod tidy && GO111MODULE=on $(GO) mod vendor
.PHONY: test-vendor
test-vendor: vendor
@diff=$$(git diff vendor/); \
if [ -n "$$diff" ]; then \
echo "Please run 'make vendor' and commit the result:"; \
echo "$${diff}"; \
exit 1; \
fi;
.PHONY: test-sqlite
test-sqlite: integrations.sqlite.test
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test
.PHONY: test-sqlite\#%
test-sqlite\#%: integrations.sqlite.test
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.run $*
.PHONY: test-sqlite-migration
test-sqlite-migration: migrations.sqlite.test
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./migrations.sqlite.test
generate-ini-mysql:
sed -e 's|{{TEST_MYSQL_HOST}}|${TEST_MYSQL_HOST}|g' \
-e 's|{{TEST_MYSQL_DBNAME}}|${TEST_MYSQL_DBNAME}|g' \
-e 's|{{TEST_MYSQL_USERNAME}}|${TEST_MYSQL_USERNAME}|g' \
-e 's|{{TEST_MYSQL_PASSWORD}}|${TEST_MYSQL_PASSWORD}|g' \
integrations/mysql.ini.tmpl > integrations/mysql.ini
for PKG in $(PACKAGES); do go test -cover -coverprofile $$GOPATH/src/$$PKG/coverage.out $$PKG || exit 1; done;
.PHONY: test-mysql
test-mysql: integrations.mysql.test generate-ini-mysql
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/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 $*
.PHONY: test-mysql-migration
test-mysql-migration: migrations.mysql.test generate-ini-mysql
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./migrations.mysql.test
generate-ini-mysql8:
sed -e 's|{{TEST_MYSQL8_HOST}}|${TEST_MYSQL8_HOST}|g' \
-e 's|{{TEST_MYSQL8_DBNAME}}|${TEST_MYSQL8_DBNAME}|g' \
-e 's|{{TEST_MYSQL8_USERNAME}}|${TEST_MYSQL8_USERNAME}|g' \
-e 's|{{TEST_MYSQL8_PASSWORD}}|${TEST_MYSQL8_PASSWORD}|g' \
integrations/mysql8.ini.tmpl > integrations/mysql8.ini
.PHONY: test-mysql8
test-mysql8: integrations.mysql8.test generate-ini-mysql8
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/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 $*
.PHONY: test-mysql8-migration
test-mysql8-migration: migrations.mysql8.test generate-ini-mysql8
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql8.ini ./migrations.mysql8.test
generate-ini-pgsql:
sed -e 's|{{TEST_PGSQL_HOST}}|${TEST_PGSQL_HOST}|g' \
-e 's|{{TEST_PGSQL_DBNAME}}|${TEST_PGSQL_DBNAME}|g' \
-e 's|{{TEST_PGSQL_USERNAME}}|${TEST_PGSQL_USERNAME}|g' \
-e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \
integrations/pgsql.ini.tmpl > integrations/pgsql.ini
test-mysql:
@echo "Not integrated yet!"
.PHONY: test-pgsql
test-pgsql: integrations.pgsql.test generate-ini-pgsql
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/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 $*
.PHONY: test-pgsql-migration
test-pgsql-migration: migrations.pgsql.test generate-ini-pgsql
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./migrations.pgsql.test
generate-ini-mssql:
sed -e 's|{{TEST_MSSQL_HOST}}|${TEST_MSSQL_HOST}|g' \
-e 's|{{TEST_MSSQL_DBNAME}}|${TEST_MSSQL_DBNAME}|g' \
-e 's|{{TEST_MSSQL_USERNAME}}|${TEST_MSSQL_USERNAME}|g' \
-e 's|{{TEST_MSSQL_PASSWORD}}|${TEST_MSSQL_PASSWORD}|g' \
integrations/mssql.ini.tmpl > integrations/mssql.ini
.PHONY: test-mssql
test-mssql: integrations.mssql.test generate-ini-mssql
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/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 $*
.PHONY: test-mssql-migration
test-mssql-migration: migrations.mssql.test generate-ini-mssql
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mssql.ini ./migrations.mssql.test
.PHONY: bench-sqlite
bench-sqlite: integrations.sqlite.test
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/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 .
.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 .
.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 .
.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
integrations.mysql.test: $(GO_SOURCES)
GO111MODULE=on $(GO) test -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.mysql.test
integrations.mysql8.test: $(GO_SOURCES)
GO111MODULE=on $(GO) test -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.mysql8.test
integrations.pgsql.test: $(GO_SOURCES)
GO111MODULE=on $(GO) test -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.pgsql.test
integrations.mssql.test: $(GO_SOURCES)
GO111MODULE=on $(GO) test -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.mssql.test
integrations.sqlite.test: $(GO_SOURCES)
GO111MODULE=on $(GO) test -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.sqlite.test -tags 'sqlite sqlite_unlock_notify'
integrations.cover.test: $(GO_SOURCES)
GO111MODULE=on $(GO) test -mod=vendor -c code.gitea.io/gitea/integrations -coverpkg $(shell echo $(PACKAGES) | tr ' ' ',') -o integrations.cover.test
.PHONY: migrations.mysql.test
migrations.mysql.test: $(GO_SOURCES)
$(GO) test -c code.gitea.io/gitea/integrations/migration-test -o migrations.mysql.test
.PHONY: migrations.mysql8.test
migrations.mysql8.test: $(GO_SOURCES)
$(GO) test -c code.gitea.io/gitea/integrations/migration-test -o migrations.mysql8.test
.PHONY: migrations.pgsql.test
migrations.pgsql.test: $(GO_SOURCES)
$(GO) test -c code.gitea.io/gitea/integrations/migration-test -o migrations.pgsql.test
.PHONY: migrations.mssql.test
migrations.mssql.test: $(GO_SOURCES)
$(GO) test -c code.gitea.io/gitea/integrations/migration-test -o migrations.mssql.test
.PHONY: migrations.sqlite.test
migrations.sqlite.test: $(GO_SOURCES)
$(GO) test -c code.gitea.io/gitea/integrations/migration-test -o migrations.sqlite.test -tags 'sqlite sqlite_unlock_notify'
test-pgsql:
@echo "Not integrated yet!"
.PHONY: check
check: test
.PHONY: install
install: $(wildcard *.go)
$(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
go install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
.PHONY: build
build: go-check generate $(EXECUTABLE)
build: $(EXECUTABLE)
$(EXECUTABLE): $(GO_SOURCES)
GO111MODULE=on $(GO) build -mod=vendor $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
$(EXECUTABLE): $(SOURCES)
go build -i -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
.PHONY: docker
docker:
docker run -ti --rm -v $(CURDIR):/srv/app/src/code.gitea.io/gitea -w /srv/app/src/code.gitea.io/gitea -e TAGS="bindata $(TAGS)" webhippie/golang:edge make clean generate build
docker build -t gitea/gitea:latest .
.PHONY: release
release: generate release-dirs release-windows release-linux release-darwin release-copy release-compress release-check
release: release-dirs release-windows release-linux release-darwin release-copy release-check
.PHONY: release-dirs
release-dirs:
@@ -422,142 +110,54 @@ release-dirs:
.PHONY: release-windows
release-windows:
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u src.techknowlogick.com/xgo; \
go get -u github.com/karalabe/xgo; \
fi
xgo -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
ifeq ($(CI),drone)
cp /build/* $(DIST)/binaries
mv /build/* $(DIST)/binaries
endif
.PHONY: release-linux
release-linux:
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u src.techknowlogick.com/xgo; \
go get -u github.com/karalabe/xgo; \
fi
xgo -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/mips64le,linux/mips,linux/mipsle' -out gitea-$(VERSION) .
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/*' -out gitea-$(VERSION) .
ifeq ($(CI),drone)
cp /build/* $(DIST)/binaries
mv /build/* $(DIST)/binaries
endif
.PHONY: release-darwin
release-darwin:
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u src.techknowlogick.com/xgo; \
go get -u github.com/karalabe/xgo; \
fi
xgo -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/*' -out gitea-$(VERSION) .
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/*' -out gitea-$(VERSION) .
ifeq ($(CI),drone)
cp /build/* $(DIST)/binaries
mv /build/* $(DIST)/binaries
endif
.PHONY: release-copy
release-copy:
cd $(DIST); for file in `find /build -type f -name "*"`; do cp $${file} ./release/; done;
$(foreach file,$(wildcard $(DIST)/binaries/$(EXECUTABLE)-*),cp $(file) $(DIST)/release/$(notdir $(file));)
.PHONY: release-check
release-check:
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "checksumming $${file}" && $(SHASUM) `echo $${file} | sed 's/^..//'` > $${file}.sha256; done;
.PHONY: release-compress
release-compress:
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/ulikunitz/xz/cmd/gxz; \
fi
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
node_modules: package-lock.json
npm install --no-save
.PHONY: npm-update
npm-update: node-check node_modules
npx updates -cu
rm -rf node_modules package-lock.json
npm install --package-lock
.PHONY: js
js: node-check $(JS_DEST)
$(JS_DEST): node_modules $(JS_SOURCES)
npx eslint web_src/js webpack.config.js
npx webpack
.PHONY: css
css: node-check $(CSS_DEST)
$(CSS_DEST): node_modules $(CSS_SOURCES)
npx stylelint web_src/less
npx lessc web_src/less/index.less public/css/index.css
$(foreach file, $(filter-out web_src/less/themes/_base.less, $(wildcard web_src/less/themes/*)),npx lessc web_src/less/themes/$(notdir $(file)) > public/css/theme-$(notdir $(call strip-suffix,$(file))).css;)
npx postcss --use autoprefixer --use cssnano --no-map --replace public/css/*
cd $(DIST)/release; $(foreach file,$(wildcard $(DIST)/release/$(EXECUTABLE)-*),sha256sum $(notdir $(file)) > $(notdir $(file)).sha256;)
.PHONY: javascripts
javascripts:
echo "'make javascripts' is deprecated, please use 'make js'"
$(MAKE) js
javascripts: public/js/index.js
.PHONY: stylesheets-check
stylesheets-check:
echo "'make stylesheets-check' is deprecated, please use 'make css'"
$(MAKE) css
.IGNORE: public/js/index.js
public/js/index.js: $(JAVASCRIPTS)
cat $< >| $@
.PHONY: generate-stylesheets
generate-stylesheets:
echo "'make generate-stylesheets' is deprecated, please use 'make css'"
$(MAKE) css
.PHONY: stylesheets
stylesheets: public/css/index.css
.PHONY: swagger-ui
swagger-ui:
rm -Rf public/vendor/assets/swagger-ui
git clone --depth=10 -b v3.13.4 --single-branch https://github.com/swagger-api/swagger-ui.git $(TMPDIR)/swagger-ui
mv $(TMPDIR)/swagger-ui/dist public/vendor/assets/swagger-ui
rm -Rf $(TMPDIR)/swagger-ui
$(SED_INPLACE) "s;http://petstore.swagger.io/v2/swagger.json;../../../swagger.v1.json;g" public/vendor/assets/swagger-ui/index.html
.IGNORE: public/css/index.css
public/css/index.css: $(STYLESHEETS)
lessc $< $@
.PHONY: update-translations
update-translations:
mkdir -p ./translations
cd ./translations && curl -L https://crowdin.com/download/project/gitea.zip > gitea.zip && unzip gitea.zip
rm ./translations/gitea.zip
$(SED_INPLACE) -e 's/="/=/g' -e 's/"$$//g' ./translations/*.ini
$(SED_INPLACE) -e 's/\\"/"/g' ./translations/*.ini
mv ./translations/*.ini ./options/locale/
rmdir ./translations
.PHONY: generate-images
generate-images:
mkdir -p $(TMPDIR)/images
inkscape -f $(PWD)/assets/logo.svg -w 880 -h 880 -e $(PWD)/public/img/gitea-lg.png
inkscape -f $(PWD)/assets/logo.svg -w 512 -h 512 -e $(PWD)/public/img/gitea-512.png
inkscape -f $(PWD)/assets/logo.svg -w 192 -h 192 -e $(PWD)/public/img/gitea-192.png
inkscape -f $(PWD)/assets/logo.svg -w 120 -h 120 -jC -i layer1 -e $(TMPDIR)/images/sm-1.png
inkscape -f $(PWD)/assets/logo.svg -w 120 -h 120 -jC -i layer2 -e $(TMPDIR)/images/sm-2.png
composite -compose atop $(TMPDIR)/images/sm-2.png $(TMPDIR)/images/sm-1.png $(PWD)/public/img/gitea-sm.png
inkscape -f $(PWD)/assets/logo.svg -w 200 -h 200 -e $(PWD)/public/img/avatar_default.png
inkscape -f $(PWD)/assets/logo.svg -w 180 -h 180 -e $(PWD)/public/img/favicon.png
inkscape -f $(PWD)/assets/logo.svg -w 128 -h 128 -e $(TMPDIR)/images/128-raw.png
inkscape -f $(PWD)/assets/logo.svg -w 64 -h 64 -e $(TMPDIR)/images/64-raw.png
inkscape -f $(PWD)/assets/logo.svg -w 32 -h 32 -jC -i layer1 -e $(TMPDIR)/images/32-1.png
inkscape -f $(PWD)/assets/logo.svg -w 32 -h 32 -jC -i layer2 -e $(TMPDIR)/images/32-2.png
composite -compose atop $(TMPDIR)/images/32-2.png $(TMPDIR)/images/32-1.png $(TMPDIR)/images/32-raw.png
inkscape -f $(PWD)/assets/logo.svg -w 16 -h 16 -jC -i layer1 -e $(TMPDIR)/images/16-raw.png
zopflipng -m -y $(TMPDIR)/images/128-raw.png $(TMPDIR)/images/128.png
zopflipng -m -y $(TMPDIR)/images/64-raw.png $(TMPDIR)/images/64.png
zopflipng -m -y $(TMPDIR)/images/32-raw.png $(TMPDIR)/images/32.png
zopflipng -m -y $(TMPDIR)/images/16-raw.png $(TMPDIR)/images/16.png
rm -f $(TMPDIR)/images/*-*.png
convert $(TMPDIR)/images/16.png $(TMPDIR)/images/32.png \
$(TMPDIR)/images/64.png $(TMPDIR)/images/128.png \
$(PWD)/public/img/favicon.ico
rm -rf $(TMPDIR)/images
$(foreach file, $(shell find public/img -type f -name '*.png'),zopflipng -m -y $(file) $(file);)
.PHONY: pr
pr:
$(GO) run contrib/pr/checkout.go $(PR)
.PHONY: golangci-lint
golangci-lint:
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
export BINARY="golangci-lint"; \
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.20.0; \
fi
golangci-lint run --timeout 5m
.PHONY: assets
assets: javascripts stylesheets

108
README.md
View File

@@ -1,63 +1,39 @@
[简体中文](README_ZH.md)
[简体中文](https://github.com/go-gitea/gitea/blob/master/README_ZH.md)
<h1> <img src="https://raw.githubusercontent.com/go-gitea/gitea/master/public/img/gitea-192.png" alt="logo" width="30" height="30"> Gitea - Git with a cup of tea</h1>
# Gitea - Git with a cup of tea
[![Build Status](https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg)](https://drone.gitea.io/go-gitea/gitea)
[![Join the Discord chat at https://discord.gg/NsatcWJ](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/NsatcWJ)
[![Join the chat at https://gitter.im/go-gitea/gitea](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-gitea/gitea?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![](https://images.microbadger.com/badges/image/gitea/gitea.svg)](https://microbadger.com/images/gitea/gitea "Get your own image badge on microbadger.com")
[![codecov](https://codecov.io/gh/go-gitea/gitea/branch/master/graph/badge.svg)](https://codecov.io/gh/go-gitea/gitea)
[![Coverage Status](https://coverage.gitea.io/badges/go-gitea/gitea/coverage.svg)](https://coverage.gitea.io/go-gitea/gitea)
[![Go Report Card](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/code.gitea.io/gitea)
[![GoDoc](https://godoc.org/code.gitea.io/gitea?status.svg)](https://godoc.org/code.gitea.io/gitea)
[![GitHub release](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest)
[![Help Contribute to Open Source](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea)
[![Become a backer/sponsor of gitea](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://crowdin.com/project/gitea)
[![Release](https://github-release-version.herokuapp.com/github/go-gitea/gitea/release.svg?style=flat)](https://github.com/go-gitea/gitea/releases/latest)
||||
|:-------------:|:-------:|:-------:|
|![Dashboard](https://i.imgur.com/3iEQsux.jpg)|![Repository](https://i.imgur.com/glqFnj8.jpg)|![Commits History](https://i.imgur.com/ad1FEpi.jpg)|
|![Profile](https://i.imgur.com/q81EcGa.jpg)|![Admin Dashboard](https://i.imgur.com/L2CQeN0.jpg)|![Diff](https://i.imgur.com/cNuvMum.jpg)|
|![Issues](https://i.imgur.com/xCYRqaF.jpg)|![Releases](https://i.imgur.com/ILpRBCe.jpg)|![Organization](https://i.imgur.com/0BHnrcL.jpg)|
||||
## Purpose
The goal of this project is to make the easiest, fastest, and most
painless way of setting up a self-hosted Git service.
Using Go, this can be done with an independent binary distribution across
**all platforms** which Go supports, including Linux, macOS, and Windows
on x86, amd64, ARM and PowerPC architectures.
Want to try it before doing anything else?
Do it [with the online demo](https://try.gitea.io/)!
This project has been
[forked](https://blog.gitea.io/2016/12/welcome-to-gitea/) from
[Gogs](https://gogs.io) since 2016.11 but changed a lot.
The goal of this project is to make the easiest, fastest, and most painless way of setting up a self-hosted Git service. Using Go, this can be done with an independent binary distribution across **all platforms** which Go supports, including Linux, macOS, and Windows on x86, amd64, ARM and PowerPC architectures. Want to try it before doing anything else? Do it [with the online demo](https://try.gitea.io/)! This project has been [forked](https://blog.gitea.io/2016/12/welcome-to-gitea/) from [Gogs](https://gogs.io).
## Building
## Notes
From the root of the source tree, run:
1. **YOU MUST READ THE [CONTRIBUTORS GUIDE](CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST.**
2. If you found a vulnerability in the project, please write privately to **security@gitea.io**. Thanks!
3. If you're interested in using our APIs, we have experimental support with [documentation](https://godoc.org/code.gitea.io/sdk/gitea).
TAGS="bindata" make build
## Docs
More info: https://docs.gitea.io/en-us/install-from-source/
## Using
./gitea web
NOTE: If you're interested in using our APIs, we have experimental
support with [documentation](https://try.gitea.io/api/swagger).
For more information and instructions about how to install Gitea please look at our [documentation](https://docs.gitea.io/en-us/). If you cannot find some specific information, then head over to our [Gitter](https://gitter.im/go-gitea/gitea) channel to chat with us.
## Contributing
Expected workflow is: Fork -> Patch -> Push -> Pull Request
NOTES:
1. **YOU MUST READ THE [CONTRIBUTORS GUIDE](CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST.**
2. If you have found a vulnerability in the project, please write privately to **security@gitea.io**. Thanks!
## Further information
For more information and instructions about how to install Gitea, please look
at our [documentation](https://docs.gitea.io/en-us/). If you have questions
that are not covered by the documentation, you can get in contact with us on
our [Discord server](https://discord.gg/NsatcWJ),
or [forum](https://discourse.gitea.io/)!
Fork -> Patch -> Push -> Pull Request
## Authors
@@ -65,48 +41,6 @@ or [forum](https://discourse.gitea.io/)!
* [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
* [Translators](options/locale/TRANSLATORS)
## Backers
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/gitea#backer)]
<a href="https://opencollective.com/gitea#backers" target="_blank"><img src="https://opencollective.com/gitea/backers.svg?width=890"></a>
## Sponsors
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/gitea#sponsor)]
<a href="https://opencollective.com/gitea/sponsor/0/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/1/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/2/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/3/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/4/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/5/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/6/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/7/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/8/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/9/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/9/avatar.svg"></a>
## FAQ
**How do you pronounce Gitea?**
Gitea is pronounced [/ɡɪti:/](https://youtu.be/EM71-2uDAoY) as in "gi-tea" with a hard g.
**Why is this not hosted on a Gitea instance?**
We're [working on it](https://github.com/go-gitea/gitea/issues/1029).
## License
This project is licensed under the MIT License.
See the [LICENSE](https://github.com/go-gitea/gitea/blob/master/LICENSE) file
for the full license text.
## Screenshots
Looking for an overview of the interface? Check it out!
|![Dashboard](https://dl.gitea.io/screenshots/home_timeline.png)|![User Profile](https://dl.gitea.io/screenshots/user_profile.png)|![Global Issues](https://dl.gitea.io/screenshots/global_issues.png)|
|:---:|:---:|:---:|
|![Branches](https://dl.gitea.io/screenshots/branches.png)|![Web Editor](https://dl.gitea.io/screenshots/web_editor.png)|![Activity](https://dl.gitea.io/screenshots/activity.png)|
|![New Migration](https://dl.gitea.io/screenshots/migration.png)|![Migrating](https://dl.gitea.io/screenshots/migration.gif)|![Pull Request View](https://image.ibb.co/e02dSb/6.png)
![Pull Request Dark](https://dl.gitea.io/screenshots/pull_requests_dark.png)|![Diff Review Dark](https://dl.gitea.io/screenshots/review_dark.png)|![Diff Dark](https://dl.gitea.io/screenshots/diff_dark.png)|
This project is licensed under the MIT License. See the [LICENSE](https://github.com/go-gitea/gitea/blob/master/LICENSE) file for the full license text.

View File

@@ -1,22 +1,25 @@
[English](README.md)
[English](https://github.com/go-gitea/gitea/blob/master/README.md)
<h1> <img src="https://raw.githubusercontent.com/go-gitea/gitea/master/public/img/gitea-192.png" alt="logo" width="30" height="30"> Gitea - Git with a cup of tea</h1>
# Gitea - Git with a cup of tea
[![Build Status](https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg)](https://drone.gitea.io/go-gitea/gitea)
[![Join the Discord chat at https://discord.gg/NsatcWJ](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/NsatcWJ)
[![Join the chat at https://gitter.im/go-gitea/gitea](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-gitea/gitea?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![](https://images.microbadger.com/badges/image/gitea/gitea.svg)](https://microbadger.com/images/gitea/gitea "Get your own image badge on microbadger.com")
[![codecov](https://codecov.io/gh/go-gitea/gitea/branch/master/graph/badge.svg)](https://codecov.io/gh/go-gitea/gitea)
[![Coverage Status](https://coverage.gitea.io/badges/go-gitea/gitea/coverage.svg)](https://coverage.gitea.io/go-gitea/gitea)
[![Go Report Card](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/code.gitea.io/gitea)
[![GoDoc](https://godoc.org/code.gitea.io/gitea?status.svg)](https://godoc.org/code.gitea.io/gitea)
[![GitHub release](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest)
[![Help Contribute to Open Source](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea)
[![Become a backer/sponsor of gitea](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://crowdin.com/project/gitea)
[![Release](https://github-release-version.herokuapp.com/github/go-gitea/gitea/release.svg?style=flat)](https://github.com/go-gitea/gitea/releases/latest)
||||
|:-------------:|:-------:|:-------:|
|![Dashboard](https://i.imgur.com/3iEQsux.jpg)|![Repository](https://i.imgur.com/glqFnj8.jpg)|![Commits History](https://i.imgur.com/ad1FEpi.jpg)|
|![Profile](https://i.imgur.com/q81EcGa.jpg)|![Admin Dashboard](https://i.imgur.com/L2CQeN0.jpg)|![Diff](https://i.imgur.com/cNuvMum.jpg)|
|![Issues](https://i.imgur.com/xCYRqaF.jpg)|![Releases](https://i.imgur.com/ILpRBCe.jpg)|![Organization](https://i.imgur.com/0BHnrcL.jpg)|
||||
## 目标
Gitea 的首要目标是创建一个极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。我们采用 Go 作为后端语言,这使我们只要生成一个可执行程序即可。并且他还支持跨平台,支持 Linux, macOS 和 Windows 以及各种架构,除了 x86amd64还包括 ARM 和 PowerPC。
Gitea的首要目标是创建一个极易安装运行非常快速安装和使用体验良好的自建 Git 服务。我们采用Go作为后端语言这使我们只要生成一个可执行程序即可。并且他还支持跨平台支持 Linux, macOS 和 Windows 以及各种架构除了x86amd64还包括 ARM 和 PowerPC。
如果您想试用一下,请访问 [在线Demo](https://try.gitea.io/)
@@ -28,7 +31,7 @@ Gitea 的首要目标是创建一个极易安装,运行非常快速,安装
## 文档
关于如何安装请访问我们的 [文档站](https://docs.gitea.io/zh-cn/),如果没有找到对应的文档,你也可以通过 [Discord - 英文](https://discord.gg/gitea) 和 QQ群 328432459 来和我们交流。
关于如何安装请访问我们的 [文档站](https://docs.gitea.io/zh-cn/),如果没有找到对应的文档,你也可以通过 [Gitter - 英文](https://gitter.im/go-gitea/gitea) 和 QQ群 328432459 来和我们交流。
## 贡献流程
@@ -43,11 +46,3 @@ Fork -> Patch -> Push -> Pull Request
## 授权许可
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/go-gitea/gitea/blob/master/LICENSE) 文件中。
## 截图
|![Dashboard](https://dl.gitea.io/screenshots/home_timeline.png)|![User Profile](https://dl.gitea.io/screenshots/user_profile.png)|![Global Issues](https://dl.gitea.io/screenshots/global_issues.png)|
|:---:|:---:|:---:|
|![Branches](https://dl.gitea.io/screenshots/branches.png)|![Web Editor](https://dl.gitea.io/screenshots/web_editor.png)|![Activity](https://dl.gitea.io/screenshots/activity.png)|
|![New Migration](https://dl.gitea.io/screenshots/migration.png)|![Migrating](https://dl.gitea.io/screenshots/migration.gif)|![Pull Request View](https://image.ibb.co/e02dSb/6.png)
![Pull Request Dark](https://dl.gitea.io/screenshots/pull_requests_dark.png)|![Diff Review Dark](https://dl.gitea.io/screenshots/review_dark.png)|![Diff Dark](https://dl.gitea.io/screenshots/diff_dark.png)|

View File

@@ -1,160 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="512"
height="512"
viewBox="0 0 135.46667 135.46667"
version="1.1"
id="svg8"
sodipodi:docname="logo.svg"
inkscape:version="0.92.1 r15371"
inkscape:export-filename=""
inkscape:export-xdpi="48.000004"
inkscape:export-ydpi="48.000004">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="0.70710678"
inkscape:cx="418.13805"
inkscape:cy="177.57445"
inkscape:document-units="mm"
inkscape:current-layer="layer2"
showgrid="false"
units="px"
width="256px"
showguides="false"
inkscape:window-width="1920"
inkscape:window-height="1137"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:pagecheckerboard="false"
inkscape:measure-start="283.373,243.952"
inkscape:measure-end="290.267,236.527">
<sodipodi:guide
position="0,0"
orientation="0,512"
id="guide3699"
inkscape:locked="false" />
<sodipodi:guide
position="135.46667,0"
orientation="-512,0"
id="guide3701"
inkscape:locked="false" />
<sodipodi:guide
position="135.46667,135.46667"
orientation="0,-512"
id="guide3703"
inkscape:locked="false" />
<sodipodi:guide
position="0,135.46667"
orientation="512,0"
id="guide3705"
inkscape:locked="false" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-161.53334)"
style="display:inline">
<path
style="fill:#609926;fill-opacity:1;stroke:#428f29;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 27.709937,195.15095 c -9.546573,-0.0272 -22.3392732,6.79805 -21.6317552,23.90397 1.105534,26.72889 25.4565952,29.20839 35.1916502,29.42301 1.068023,5.01357 12.521798,22.30563 21.001818,23.21667 h 37.15277 c 22.27763,-1.66785 38.9607,-75.75671 26.59321,-76.03825 -46.781583,2.47691 -49.995146,2.13838 -88.599758,0 -2.495053,-0.0266 -5.972321,-0.49474 -9.707935,-0.5054 z m 2.491319,9.45886 c 1.351378,13.69267 3.555849,21.70359 8.018216,33.94345 -11.382872,-1.50473 -21.069822,-5.22443 -22.851515,-19.10984 -0.950962,-7.4112 2.390428,-15.16769 14.833299,-14.83361 z"
id="path3722"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sscccccsccsc" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Layer 2"
style="display:inline">
<rect
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.24757317;stroke-opacity:1"
id="rect4599"
width="34.762054"
height="34.762054"
x="87.508659"
y="18.291576"
transform="rotate(25.914715)"
ry="5.4825778" />
<path
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26644793px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 79.804947,57.359056 3.241146,1.609954 V 35.255731 h -3.262698 z"
id="path4525"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Layer 3"
style="display:inline">
<g
style="display:inline"
id="g4539">
<circle
transform="rotate(-19.796137)"
r="3.4745038"
cy="90.077766"
cx="49.064713"
id="path4606"
style="fill:#609926;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" />
<circle
transform="rotate(-19.796137)"
r="3.4745038"
cy="102.1049"
cx="36.810425"
id="path4606-3"
style="fill:#609926;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" />
<circle
transform="rotate(-19.796137)"
r="3.4745038"
cy="111.43928"
cx="46.484283"
id="path4606-1"
style="fill:#609926;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" />
<rect
transform="rotate(26.024158)"
y="18.061695"
x="97.333458"
height="27.261492"
width="2.6726954"
id="rect4629-8"
style="fill:#609926;fill-opacity:1;stroke:none;stroke-width:0.27444693;stroke-opacity:1" />
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="path4514"
d="m 76.558096,68.116343 c 12.97589,6.395378 13.012989,4.101862 4.890858,20.907244"
style="fill:none;stroke:#609926;stroke-width:2.68000007;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -6,33 +6,23 @@
package cmd
import (
"errors"
"fmt"
"os"
"text/tabwriter"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth/oauth2"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
pwd "code.gitea.io/gitea/modules/password"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/setting"
)
var (
// CmdAdmin represents the available admin sub-command.
CmdAdmin = cli.Command{
Name: "admin",
Usage: "Command line interface to perform common administrative operations",
Usage: "Perform admin operations on command line",
Description: `Allow using internal logic of Gitea without hacking into the source code
to make automatic initialization process more smoothly`,
Subcommands: []cli.Command{
subcmdCreateUser,
subcmdChangePassword,
subcmdRepoSyncReleases,
subcmdRegenerate,
subcmdAuth,
},
}
@@ -43,519 +33,63 @@ var (
Flags: []cli.Flag{
cli.StringFlag{
Name: "name",
Usage: "Username. DEPRECATED: use username instead",
},
cli.StringFlag{
Name: "username",
Value: "",
Usage: "Username",
},
cli.StringFlag{
Name: "password",
Value: "",
Usage: "User password",
},
cli.StringFlag{
Name: "email",
Value: "",
Usage: "User email address",
},
cli.BoolFlag{
Name: "admin",
Usage: "User is an admin",
},
cli.BoolFlag{
Name: "random-password",
Usage: "Generate a random password for the user",
},
cli.BoolFlag{
Name: "must-change-password",
Usage: "Set this option to false to prevent forcing the user to change their password after initial login, (Default: true)",
},
cli.IntFlag{
Name: "random-password-length",
Usage: "Length of the random password to be generated",
Value: 12,
},
cli.BoolFlag{
Name: "access-token",
Usage: "Generate access token for the user",
},
},
}
subcmdChangePassword = cli.Command{
Name: "change-password",
Usage: "Change a user's password",
Action: runChangePassword,
Flags: []cli.Flag{
cli.StringFlag{
Name: "username,u",
Value: "",
Usage: "The user to change password for",
},
cli.StringFlag{
Name: "password,p",
Value: "",
Usage: "New password to set for user",
Name: "config, c",
Value: "custom/conf/app.ini",
Usage: "Custom configuration file path",
},
},
}
subcmdRepoSyncReleases = cli.Command{
Name: "repo-sync-releases",
Usage: "Synchronize repository releases with tags",
Action: runRepoSyncReleases,
}
subcmdRegenerate = cli.Command{
Name: "regenerate",
Usage: "Regenerate specific files",
Subcommands: []cli.Command{
microcmdRegenHooks,
microcmdRegenKeys,
},
}
microcmdRegenHooks = cli.Command{
Name: "hooks",
Usage: "Regenerate git-hooks",
Action: runRegenerateHooks,
}
microcmdRegenKeys = cli.Command{
Name: "keys",
Usage: "Regenerate authorized_keys file",
Action: runRegenerateKeys,
}
subcmdAuth = cli.Command{
Name: "auth",
Usage: "Modify external auth providers",
Subcommands: []cli.Command{
microcmdAuthAddOauth,
microcmdAuthUpdateOauth,
cmdAuthAddLdapBindDn,
cmdAuthUpdateLdapBindDn,
cmdAuthAddLdapSimpleAuth,
cmdAuthUpdateLdapSimpleAuth,
microcmdAuthList,
microcmdAuthDelete,
},
}
microcmdAuthList = cli.Command{
Name: "list",
Usage: "List auth sources",
Action: runListAuth,
}
idFlag = cli.Int64Flag{
Name: "id",
Usage: "ID of authentication source",
}
microcmdAuthDelete = cli.Command{
Name: "delete",
Usage: "Delete specific auth source",
Flags: []cli.Flag{idFlag},
Action: runDeleteAuth,
}
oauthCLIFlags = []cli.Flag{
cli.StringFlag{
Name: "name",
Value: "",
Usage: "Application Name",
},
cli.StringFlag{
Name: "provider",
Value: "",
Usage: "OAuth2 Provider",
},
cli.StringFlag{
Name: "key",
Value: "",
Usage: "Client ID (Key)",
},
cli.StringFlag{
Name: "secret",
Value: "",
Usage: "Client Secret",
},
cli.StringFlag{
Name: "auto-discover-url",
Value: "",
Usage: "OpenID Connect Auto Discovery URL (only required when using OpenID Connect as provider)",
},
cli.StringFlag{
Name: "use-custom-urls",
Value: "false",
Usage: "Use custom URLs for GitLab/GitHub OAuth endpoints",
},
cli.StringFlag{
Name: "custom-auth-url",
Value: "",
Usage: "Use a custom Authorization URL (option for GitLab/GitHub)",
},
cli.StringFlag{
Name: "custom-token-url",
Value: "",
Usage: "Use a custom Token URL (option for GitLab/GitHub)",
},
cli.StringFlag{
Name: "custom-profile-url",
Value: "",
Usage: "Use a custom Profile URL (option for GitLab/GitHub)",
},
cli.StringFlag{
Name: "custom-email-url",
Value: "",
Usage: "Use a custom Email URL (option for GitHub)",
},
}
microcmdAuthUpdateOauth = cli.Command{
Name: "update-oauth",
Usage: "Update existing Oauth authentication source",
Action: runUpdateOauth,
Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...),
}
microcmdAuthAddOauth = cli.Command{
Name: "add-oauth",
Usage: "Add new Oauth authentication source",
Action: runAddOauth,
Flags: oauthCLIFlags,
}
)
func runChangePassword(c *cli.Context) error {
if err := argsSet(c, "username", "password"); err != nil {
return err
}
if err := initDB(); err != nil {
return err
}
if !pwd.IsComplexEnough(c.String("password")) {
return errors.New("Password does not meet complexity requirements")
}
uname := c.String("username")
user, err := models.GetUserByName(uname)
if err != nil {
return err
}
if user.Salt, err = models.GetUserSalt(); err != nil {
return err
}
user.HashPassword(c.String("password"))
if err := models.UpdateUserCols(user, "passwd", "salt"); err != nil {
return err
}
fmt.Printf("%s's password has been successfully updated!\n", user.Name)
return nil
}
func runCreateUser(c *cli.Context) error {
if err := argsSet(c, "email"); err != nil {
return err
if !c.IsSet("name") {
return fmt.Errorf("Username is not specified")
} else if !c.IsSet("password") {
return fmt.Errorf("Password is not specified")
} else if !c.IsSet("email") {
return fmt.Errorf("Email is not specified")
}
if c.IsSet("name") && c.IsSet("username") {
return errors.New("Cannot set both --name and --username flags")
}
if !c.IsSet("name") && !c.IsSet("username") {
return errors.New("One of --name or --username flags must be set")
if c.IsSet("config") {
setting.CustomConf = c.String("config")
}
if c.IsSet("password") && c.IsSet("random-password") {
return errors.New("cannot set both -random-password and -password flags")
setting.NewContext()
models.LoadConfigs()
setting.NewXORMLogService(false)
if err := models.SetEngine(); err != nil {
return fmt.Errorf("models.SetEngine: %v", err)
}
var username string
if c.IsSet("username") {
username = c.String("username")
} else {
username = c.String("name")
fmt.Fprintf(os.Stderr, "--name flag is deprecated. Use --username instead.\n")
}
if err := initDB(); err != nil {
return err
}
var password string
if c.IsSet("password") {
password = c.String("password")
} else if c.IsSet("random-password") {
var err error
password, err = pwd.Generate(c.Int("random-password-length"))
if err != nil {
return err
}
fmt.Printf("generated random password is '%s'\n", password)
} else {
return errors.New("must set either password or random-password flag")
}
// always default to true
var changePassword = true
// If this is the first user being created.
// Take it as the admin and don't force a password update.
if n := models.CountUsers(); n == 0 {
changePassword = false
}
if c.IsSet("must-change-password") {
changePassword = c.Bool("must-change-password")
}
u := &models.User{
Name: username,
Email: c.String("email"),
Passwd: password,
IsActive: true,
IsAdmin: c.Bool("admin"),
MustChangePassword: changePassword,
Theme: setting.UI.DefaultTheme,
}
if err := models.CreateUser(u); err != nil {
if err := models.CreateUser(&models.User{
Name: c.String("name"),
Email: c.String("email"),
Passwd: c.String("password"),
IsActive: true,
IsAdmin: c.Bool("admin"),
}); err != nil {
return fmt.Errorf("CreateUser: %v", err)
}
if c.Bool("access-token") {
t := &models.AccessToken{
Name: "gitea-admin",
UID: u.ID,
}
if err := models.NewAccessToken(t); err != nil {
return err
}
fmt.Printf("Access token was successfully created... %s\n", t.Token)
}
fmt.Printf("New user '%s' has been successfully created!\n", username)
fmt.Printf("New user '%s' has been successfully created!\n", c.String("name"))
return nil
}
func runRepoSyncReleases(c *cli.Context) error {
if err := initDB(); err != nil {
return err
}
log.Trace("Synchronizing repository releases (this may take a while)")
for page := 1; ; page++ {
repos, count, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
Page: page,
PageSize: models.RepositoryListDefaultPageSize,
Private: true,
})
if err != nil {
return fmt.Errorf("SearchRepositoryByName: %v", err)
}
if len(repos) == 0 {
break
}
log.Trace("Processing next %d repos of %d", len(repos), count)
for _, repo := range repos {
log.Trace("Synchronizing repo %s with path %s", repo.FullName(), repo.RepoPath())
gitRepo, err := git.OpenRepository(repo.RepoPath())
if err != nil {
log.Warn("OpenRepository: %v", err)
continue
}
oldnum, err := getReleaseCount(repo.ID)
if err != nil {
log.Warn(" GetReleaseCountByRepoID: %v", err)
}
log.Trace(" currentNumReleases is %d, running SyncReleasesWithTags", oldnum)
if err = repository.SyncReleasesWithTags(repo, gitRepo); err != nil {
log.Warn(" SyncReleasesWithTags: %v", err)
gitRepo.Close()
continue
}
count, err = getReleaseCount(repo.ID)
if err != nil {
log.Warn(" GetReleaseCountByRepoID: %v", err)
gitRepo.Close()
continue
}
log.Trace(" repo %s releases synchronized to tags: from %d to %d",
repo.FullName(), oldnum, count)
gitRepo.Close()
}
}
return nil
}
func getReleaseCount(id int64) (int64, error) {
return models.GetReleaseCountByRepoID(
id,
models.FindReleasesOptions{
IncludeTags: true,
},
)
}
func runRegenerateHooks(c *cli.Context) error {
if err := initDB(); err != nil {
return err
}
return models.SyncRepositoryHooks()
}
func runRegenerateKeys(c *cli.Context) error {
if err := initDB(); err != nil {
return err
}
return models.RewriteAllPublicKeys()
}
func parseOAuth2Config(c *cli.Context) *models.OAuth2Config {
var customURLMapping *oauth2.CustomURLMapping
if c.IsSet("use-custom-urls") {
customURLMapping = &oauth2.CustomURLMapping{
TokenURL: c.String("custom-token-url"),
AuthURL: c.String("custom-auth-url"),
ProfileURL: c.String("custom-profile-url"),
EmailURL: c.String("custom-email-url"),
}
} else {
customURLMapping = nil
}
return &models.OAuth2Config{
Provider: c.String("provider"),
ClientID: c.String("key"),
ClientSecret: c.String("secret"),
OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
CustomURLMapping: customURLMapping,
}
}
func runAddOauth(c *cli.Context) error {
if err := initDB(); err != nil {
return err
}
return models.CreateLoginSource(&models.LoginSource{
Type: models.LoginOAuth2,
Name: c.String("name"),
IsActived: true,
Cfg: parseOAuth2Config(c),
})
}
func runUpdateOauth(c *cli.Context) error {
if !c.IsSet("id") {
return fmt.Errorf("--id flag is missing")
}
if err := initDB(); err != nil {
return err
}
source, err := models.GetLoginSourceByID(c.Int64("id"))
if err != nil {
return err
}
oAuth2Config := source.OAuth2()
if c.IsSet("name") {
source.Name = c.String("name")
}
if c.IsSet("provider") {
oAuth2Config.Provider = c.String("provider")
}
if c.IsSet("key") {
oAuth2Config.ClientID = c.String("key")
}
if c.IsSet("secret") {
oAuth2Config.ClientSecret = c.String("secret")
}
if c.IsSet("auto-discover-url") {
oAuth2Config.OpenIDConnectAutoDiscoveryURL = c.String("auto-discover-url")
}
// update custom URL mapping
var customURLMapping = &oauth2.CustomURLMapping{}
if oAuth2Config.CustomURLMapping != nil {
customURLMapping.TokenURL = oAuth2Config.CustomURLMapping.TokenURL
customURLMapping.AuthURL = oAuth2Config.CustomURLMapping.AuthURL
customURLMapping.ProfileURL = oAuth2Config.CustomURLMapping.ProfileURL
customURLMapping.EmailURL = oAuth2Config.CustomURLMapping.EmailURL
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-token-url") {
customURLMapping.TokenURL = c.String("custom-token-url")
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-auth-url") {
customURLMapping.AuthURL = c.String("custom-auth-url")
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-profile-url") {
customURLMapping.ProfileURL = c.String("custom-profile-url")
}
if c.IsSet("use-custom-urls") && c.IsSet("custom-email-url") {
customURLMapping.EmailURL = c.String("custom-email-url")
}
oAuth2Config.CustomURLMapping = customURLMapping
source.Cfg = oAuth2Config
return models.UpdateSource(source)
}
func runListAuth(c *cli.Context) error {
if err := initDB(); err != nil {
return err
}
loginSources, err := models.LoginSources()
if err != nil {
return err
}
// loop through each source and print
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.AlignRight)
fmt.Fprintf(w, "ID\tName\tType\tEnabled\n")
for _, source := range loginSources {
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, models.LoginNames[source.Type], source.IsActived)
}
w.Flush()
return nil
}
func runDeleteAuth(c *cli.Context) error {
if !c.IsSet("id") {
return fmt.Errorf("--id flag is missing")
}
if err := initDB(); err != nil {
return err
}
source, err := models.GetLoginSourceByID(c.Int64("id"))
if err != nil {
return err
}
return models.DeleteSource(source)
}

View File

@@ -1,359 +0,0 @@
// Copyright 2019 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 cmd
import (
"fmt"
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth/ldap"
"github.com/urfave/cli"
)
type (
authService struct {
initDB func() error
createLoginSource func(loginSource *models.LoginSource) error
updateLoginSource func(loginSource *models.LoginSource) error
getLoginSourceByID func(id int64) (*models.LoginSource, error)
}
)
var (
commonLdapCLIFlags = []cli.Flag{
cli.StringFlag{
Name: "name",
Usage: "Authentication name.",
},
cli.BoolFlag{
Name: "not-active",
Usage: "Deactivate the authentication source.",
},
cli.StringFlag{
Name: "security-protocol",
Usage: "Security protocol name.",
},
cli.BoolFlag{
Name: "skip-tls-verify",
Usage: "Disable TLS verification.",
},
cli.StringFlag{
Name: "host",
Usage: "The address where the LDAP server can be reached.",
},
cli.IntFlag{
Name: "port",
Usage: "The port to use when connecting to the LDAP server.",
},
cli.StringFlag{
Name: "user-search-base",
Usage: "The LDAP base at which user accounts will be searched for.",
},
cli.StringFlag{
Name: "user-filter",
Usage: "An LDAP filter declaring how to find the user record that is attempting to authenticate.",
},
cli.StringFlag{
Name: "admin-filter",
Usage: "An LDAP filter specifying if a user should be given administrator privileges.",
},
cli.StringFlag{
Name: "username-attribute",
Usage: "The attribute of the users LDAP record containing the user name.",
},
cli.StringFlag{
Name: "firstname-attribute",
Usage: "The attribute of the users LDAP record containing the users first name.",
},
cli.StringFlag{
Name: "surname-attribute",
Usage: "The attribute of the users LDAP record containing the users surname.",
},
cli.StringFlag{
Name: "email-attribute",
Usage: "The attribute of the users LDAP record containing the users email address.",
},
cli.StringFlag{
Name: "public-ssh-key-attribute",
Usage: "The attribute of the users LDAP record containing the users public ssh key.",
},
}
ldapBindDnCLIFlags = append(commonLdapCLIFlags,
cli.StringFlag{
Name: "bind-dn",
Usage: "The DN to bind to the LDAP server with when searching for the user.",
},
cli.StringFlag{
Name: "bind-password",
Usage: "The password for the Bind DN, if any.",
},
cli.BoolFlag{
Name: "attributes-in-bind",
Usage: "Fetch attributes in bind DN context.",
},
cli.BoolFlag{
Name: "synchronize-users",
Usage: "Enable user synchronization.",
},
cli.UintFlag{
Name: "page-size",
Usage: "Search page size.",
})
ldapSimpleAuthCLIFlags = append(commonLdapCLIFlags,
cli.StringFlag{
Name: "user-dn",
Usage: "The users DN.",
})
cmdAuthAddLdapBindDn = cli.Command{
Name: "add-ldap",
Usage: "Add new LDAP (via Bind DN) authentication source",
Action: func(c *cli.Context) error {
return newAuthService().addLdapBindDn(c)
},
Flags: ldapBindDnCLIFlags,
}
cmdAuthUpdateLdapBindDn = cli.Command{
Name: "update-ldap",
Usage: "Update existing LDAP (via Bind DN) authentication source",
Action: func(c *cli.Context) error {
return newAuthService().updateLdapBindDn(c)
},
Flags: append([]cli.Flag{idFlag}, ldapBindDnCLIFlags...),
}
cmdAuthAddLdapSimpleAuth = cli.Command{
Name: "add-ldap-simple",
Usage: "Add new LDAP (simple auth) authentication source",
Action: func(c *cli.Context) error {
return newAuthService().addLdapSimpleAuth(c)
},
Flags: ldapSimpleAuthCLIFlags,
}
cmdAuthUpdateLdapSimpleAuth = cli.Command{
Name: "update-ldap-simple",
Usage: "Update existing LDAP (simple auth) authentication source",
Action: func(c *cli.Context) error {
return newAuthService().updateLdapSimpleAuth(c)
},
Flags: append([]cli.Flag{idFlag}, ldapSimpleAuthCLIFlags...),
}
)
// newAuthService creates a service with default functions.
func newAuthService() *authService {
return &authService{
initDB: initDB,
createLoginSource: models.CreateLoginSource,
updateLoginSource: models.UpdateSource,
getLoginSourceByID: models.GetLoginSourceByID,
}
}
// parseLoginSource assigns values on loginSource according to command line flags.
func parseLoginSource(c *cli.Context, loginSource *models.LoginSource) {
if c.IsSet("name") {
loginSource.Name = c.String("name")
}
if c.IsSet("not-active") {
loginSource.IsActived = !c.Bool("not-active")
}
if c.IsSet("synchronize-users") {
loginSource.IsSyncEnabled = c.Bool("synchronize-users")
}
}
// parseLdapConfig assigns values on config according to command line flags.
func parseLdapConfig(c *cli.Context, config *models.LDAPConfig) error {
if c.IsSet("name") {
config.Source.Name = c.String("name")
}
if c.IsSet("host") {
config.Source.Host = c.String("host")
}
if c.IsSet("port") {
config.Source.Port = c.Int("port")
}
if c.IsSet("security-protocol") {
p, ok := findLdapSecurityProtocolByName(c.String("security-protocol"))
if !ok {
return fmt.Errorf("Unknown security protocol name: %s", c.String("security-protocol"))
}
config.Source.SecurityProtocol = p
}
if c.IsSet("skip-tls-verify") {
config.Source.SkipVerify = c.Bool("skip-tls-verify")
}
if c.IsSet("bind-dn") {
config.Source.BindDN = c.String("bind-dn")
}
if c.IsSet("user-dn") {
config.Source.UserDN = c.String("user-dn")
}
if c.IsSet("bind-password") {
config.Source.BindPassword = c.String("bind-password")
}
if c.IsSet("user-search-base") {
config.Source.UserBase = c.String("user-search-base")
}
if c.IsSet("username-attribute") {
config.Source.AttributeUsername = c.String("username-attribute")
}
if c.IsSet("firstname-attribute") {
config.Source.AttributeName = c.String("firstname-attribute")
}
if c.IsSet("surname-attribute") {
config.Source.AttributeSurname = c.String("surname-attribute")
}
if c.IsSet("email-attribute") {
config.Source.AttributeMail = c.String("email-attribute")
}
if c.IsSet("attributes-in-bind") {
config.Source.AttributesInBind = c.Bool("attributes-in-bind")
}
if c.IsSet("public-ssh-key-attribute") {
config.Source.AttributeSSHPublicKey = c.String("public-ssh-key-attribute")
}
if c.IsSet("page-size") {
config.Source.SearchPageSize = uint32(c.Uint("page-size"))
}
if c.IsSet("user-filter") {
config.Source.Filter = c.String("user-filter")
}
if c.IsSet("admin-filter") {
config.Source.AdminFilter = c.String("admin-filter")
}
return nil
}
// findLdapSecurityProtocolByName finds security protocol by its name ignoring case.
// It returns the value of the security protocol and if it was found.
func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
for i, n := range models.SecurityProtocolNames {
if strings.EqualFold(name, n) {
return i, true
}
}
return 0, false
}
// getLoginSource gets the login source by its id defined in the command line flags.
// It returns an error if the id is not set, does not match any source or if the source is not of expected type.
func (a *authService) getLoginSource(c *cli.Context, loginType models.LoginType) (*models.LoginSource, error) {
if err := argsSet(c, "id"); err != nil {
return nil, err
}
loginSource, err := a.getLoginSourceByID(c.Int64("id"))
if err != nil {
return nil, err
}
if loginSource.Type != loginType {
return nil, fmt.Errorf("Invalid authentication type. expected: %s, actual: %s", models.LoginNames[loginType], models.LoginNames[loginSource.Type])
}
return loginSource, nil
}
// addLdapBindDn adds a new LDAP via Bind DN authentication source.
func (a *authService) addLdapBindDn(c *cli.Context) error {
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-search-base", "user-filter", "email-attribute"); err != nil {
return err
}
if err := a.initDB(); err != nil {
return err
}
loginSource := &models.LoginSource{
Type: models.LoginLDAP,
IsActived: true, // active by default
Cfg: &models.LDAPConfig{
Source: &ldap.Source{
Enabled: true, // always true
},
},
}
parseLoginSource(c, loginSource)
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
return err
}
return a.createLoginSource(loginSource)
}
// updateLdapBindDn updates a new LDAP via Bind DN authentication source.
func (a *authService) updateLdapBindDn(c *cli.Context) error {
if err := a.initDB(); err != nil {
return err
}
loginSource, err := a.getLoginSource(c, models.LoginLDAP)
if err != nil {
return err
}
parseLoginSource(c, loginSource)
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
return err
}
return a.updateLoginSource(loginSource)
}
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-dn", "user-filter", "email-attribute"); err != nil {
return err
}
if err := a.initDB(); err != nil {
return err
}
loginSource := &models.LoginSource{
Type: models.LoginDLDAP,
IsActived: true, // active by default
Cfg: &models.LDAPConfig{
Source: &ldap.Source{
Enabled: true, // always true
},
},
}
parseLoginSource(c, loginSource)
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
return err
}
return a.createLoginSource(loginSource)
}
// updateLdapBindDn updates a new LDAP (simple auth) authentication source.
func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
if err := a.initDB(); err != nil {
return err
}
loginSource, err := a.getLoginSource(c, models.LoginDLDAP)
if err != nil {
return err
}
parseLoginSource(c, loginSource)
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
return err
}
return a.updateLoginSource(loginSource)
}

File diff suppressed because it is too large Load Diff

View File

@@ -90,16 +90,16 @@ func pemBlockForKey(priv interface{}) *pem.Block {
}
}
func runCert(c *cli.Context) error {
if err := argsSet(c, "host"); err != nil {
return err
func runCert(ctx *cli.Context) error {
if len(ctx.String("host")) == 0 {
log.Fatal("Missing required --host parameter")
}
var priv interface{}
var err error
switch c.String("ecdsa-curve") {
switch ctx.String("ecdsa-curve") {
case "":
priv, err = rsa.GenerateKey(rand.Reader, c.Int("rsa-bits"))
priv, err = rsa.GenerateKey(rand.Reader, ctx.Int("rsa-bits"))
case "P224":
priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
case "P256":
@@ -109,23 +109,23 @@ func runCert(c *cli.Context) error {
case "P521":
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
default:
log.Fatalf("Unrecognized elliptic curve: %q", c.String("ecdsa-curve"))
log.Fatalf("Unrecognized elliptic curve: %q", ctx.String("ecdsa-curve"))
}
if err != nil {
log.Fatalf("Failed to generate private key: %v", err)
}
var notBefore time.Time
if startDate := c.String("start-date"); startDate != "" {
notBefore, err = time.Parse("Jan 2 15:04:05 2006", startDate)
if len(ctx.String("start-date")) == 0 {
notBefore = time.Now()
} else {
notBefore, err = time.Parse("Jan 2 15:04:05 2006", ctx.String("start-date"))
if err != nil {
log.Fatalf("Failed to parse creation date: %v", err)
}
} else {
notBefore = time.Now()
}
notAfter := notBefore.Add(c.Duration("duration"))
notAfter := notBefore.Add(ctx.Duration("duration"))
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
@@ -147,7 +147,7 @@ func runCert(c *cli.Context) error {
BasicConstraintsValid: true,
}
hosts := strings.Split(c.String("host"), ",")
hosts := strings.Split(ctx.String("host"), ",")
for _, h := range hosts {
if ip := net.ParseIP(h); ip != nil {
template.IPAddresses = append(template.IPAddresses, ip)
@@ -156,7 +156,7 @@ func runCert(c *cli.Context) error {
}
}
if c.Bool("ca") {
if ctx.Bool("ca") {
template.IsCA = true
template.KeyUsage |= x509.KeyUsageCertSign
}
@@ -170,28 +170,17 @@ func runCert(c *cli.Context) error {
if err != nil {
log.Fatalf("Failed to open cert.pem for writing: %v", err)
}
err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
if err != nil {
log.Fatalf("Failed to encode certificate: %v", err)
}
err = certOut.Close()
if err != nil {
log.Fatalf("Failed to write cert: %v", err)
}
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
certOut.Close()
log.Println("Written cert.pem")
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
log.Fatalf("Failed to open key.pem for writing: %v", err)
}
err = pem.Encode(keyOut, pemBlockForKey(priv))
if err != nil {
log.Fatalf("Failed to encode key: %v", err)
}
err = keyOut.Close()
if err != nil {
log.Fatalf("Failed to write key: %v", err)
}
pem.Encode(keyOut, pemBlockForKey(priv))
keyOut.Close()
log.Println("Written key.pem")
return nil
}

View File

@@ -1,48 +0,0 @@
// Copyright 2018 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 cmd provides subcommands to the gitea binary - such as "web" or
// "admin".
package cmd
import (
"errors"
"fmt"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/urfave/cli"
)
// argsSet checks that all the required arguments are set. args is a list of
// arguments that must be set in the passed Context.
func argsSet(c *cli.Context, args ...string) error {
for _, a := range args {
if !c.IsSet(a) {
return errors.New(a + " is not set")
}
if util.IsEmptyString(a) {
return errors.New(a + " is required")
}
}
return nil
}
func initDB() error {
return initDBDisableConsole(false)
}
func initDBDisableConsole(disableConsole bool) error {
setting.NewContext()
setting.InitDBConfig()
setting.NewXORMLogService(disableConsole)
if err := models.SetEngine(); err != nil {
return fmt.Errorf("models.SetEngine: %v", err)
}
return nil
}

View File

@@ -1,49 +0,0 @@
// Copyright 2019 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 cmd
import (
"fmt"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli"
)
// CmdConvert represents the available convert sub-command.
var CmdConvert = cli.Command{
Name: "convert",
Usage: "Convert the database",
Description: "A command to convert an existing MySQL database from utf8 to utf8mb4",
Action: runConvert,
}
func runConvert(ctx *cli.Context) error {
if err := initDB(); err != nil {
return err
}
log.Trace("AppPath: %s", setting.AppPath)
log.Trace("AppWorkPath: %s", setting.AppWorkPath)
log.Trace("Custom path: %s", setting.CustomPath)
log.Trace("Log path: %s", setting.LogRootPath)
setting.InitDBConfig()
if !setting.Database.UseMySQL {
fmt.Println("This command can only be used with a MySQL database")
return nil
}
if err := models.ConvertUtf8ToUtf8mb4(); err != nil {
log.Fatal("Failed to convert database from utf8 to utf8mb4: %v", err)
return err
}
fmt.Println("Converted successfully, please confirm your database's character set is now utf8mb4")
return nil
}

View File

@@ -8,17 +8,16 @@ package cmd
import (
"fmt"
"io/ioutil"
"log"
"os"
"path"
"path/filepath"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/unknwon/cae/zip"
"github.com/unknwon/com"
"github.com/Unknwon/cae/zip"
"github.com/Unknwon/com"
"github.com/urfave/cli"
)
@@ -31,12 +30,12 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
Action: runDump,
Flags: []cli.Flag{
cli.StringFlag{
Name: "file, f",
Value: fmt.Sprintf("gitea-dump-%d.zip", time.Now().Unix()),
Usage: "Name of the dump file which will be created.",
Name: "config, c",
Value: "custom/conf/app.ini",
Usage: "Custom configuration file path",
},
cli.BoolFlag{
Name: "verbose, V",
Name: "verbose, v",
Usage: "Show process details",
},
cli.StringFlag{
@@ -48,21 +47,16 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
Name: "database, d",
Usage: "Specify the database SQL syntax",
},
cli.BoolFlag{
Name: "skip-repository, R",
Usage: "Skip the repository dumping",
},
},
}
func fatal(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, format+"\n", args...)
log.Fatal(format, args...)
}
func runDump(ctx *cli.Context) error {
if ctx.IsSet("config") {
setting.CustomConf = ctx.String("config")
}
setting.NewContext()
setting.NewServices() // cannot access session settings otherwise
models.LoadConfigs()
err := models.SetEngine()
if err != nil {
@@ -71,105 +65,90 @@ func runDump(ctx *cli.Context) error {
tmpDir := ctx.String("tempdir")
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
fatal("Path does not exist: %s", tmpDir)
log.Fatalf("Path does not exist: %s", tmpDir)
}
tmpWorkDir, err := ioutil.TempDir(tmpDir, "gitea-dump-")
TmpWorkDir, err := ioutil.TempDir(tmpDir, "gitea-dump-")
if err != nil {
fatal("Failed to create tmp work directory: %v", err)
log.Fatalf("Failed to create tmp work directory: %v", err)
}
log.Info("Creating tmp work dir: %s", tmpWorkDir)
log.Printf("Creating tmp work dir: %s", TmpWorkDir)
// work-around #1103
if os.Getenv("TMPDIR") == "" {
os.Setenv("TMPDIR", tmpWorkDir)
}
dbDump := path.Join(tmpWorkDir, "gitea-db.sql")
fileName := ctx.String("file")
log.Info("Packing dump files...")
z, err := zip.Create(fileName)
if err != nil {
fatal("Failed to create %s: %v", fileName, err)
}
reposDump := path.Join(TmpWorkDir, "gitea-repo.zip")
dbDump := path.Join(TmpWorkDir, "gitea-db.sql")
log.Printf("Dumping local repositories...%s", setting.RepoRootPath)
zip.Verbose = ctx.Bool("verbose")
if ctx.IsSet("skip-repository") {
log.Info("Skip dumping local repositories")
} else {
log.Info("Dumping local repositories...%s", setting.RepoRootPath)
reposDump := path.Join(tmpWorkDir, "gitea-repo.zip")
if err := zip.PackTo(setting.RepoRootPath, reposDump, true); err != nil {
fatal("Failed to dump local repositories: %v", err)
}
if err := z.AddFile("gitea-repo.zip", reposDump); err != nil {
fatal("Failed to include gitea-repo.zip: %v", err)
}
if err := zip.PackTo(setting.RepoRootPath, reposDump, true); err != nil {
log.Fatalf("Failed to dump local repositories: %v", err)
}
targetDBType := ctx.String("database")
if len(targetDBType) > 0 && targetDBType != setting.Database.Type {
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
if len(targetDBType) > 0 && targetDBType != models.DbCfg.Type {
log.Printf("Dumping database %s => %s...", models.DbCfg.Type, targetDBType)
} else {
log.Info("Dumping database...")
log.Printf("Dumping database...")
}
if err := models.DumpDatabase(dbDump, targetDBType); err != nil {
fatal("Failed to dump database: %v", err)
log.Fatalf("Failed to dump database: %v", err)
}
fileName := fmt.Sprintf("gitea-dump-%d.zip", time.Now().Unix())
log.Printf("Packing dump files...")
z, err := zip.Create(fileName)
if err != nil {
log.Fatalf("Failed to create %s: %v", fileName, err)
}
if err := z.AddFile("gitea-repo.zip", reposDump); err != nil {
log.Fatalf("Failed to include gitea-repo.zip: %v", err)
}
if err := z.AddFile("gitea-db.sql", dbDump); err != nil {
fatal("Failed to include gitea-db.sql: %v", err)
log.Fatalf("Failed to include gitea-db.sql: %v", err)
}
if len(setting.CustomConf) > 0 {
log.Info("Adding custom configuration file from %s", setting.CustomConf)
if err := z.AddFile("app.ini", setting.CustomConf); err != nil {
fatal("Failed to include specified app.ini: %v", err)
}
}
customDir, err := os.Stat(setting.CustomPath)
if err == nil && customDir.IsDir() {
if err := z.AddDir("custom", setting.CustomPath); err != nil {
fatal("Failed to include custom: %v", err)
log.Fatalf("Failed to include custom: %v", err)
}
} else {
log.Info("Custom dir %s doesn't exist, skipped", setting.CustomPath)
log.Printf("Custom dir %s doesn't exist, skipped", setting.CustomPath)
}
if com.IsExist(setting.AppDataPath) {
log.Info("Packing data directory...%s", setting.AppDataPath)
log.Printf("Packing data directory...%s", setting.AppDataPath)
var sessionAbsPath string
if setting.SessionConfig.Provider == "file" {
sessionAbsPath = setting.SessionConfig.ProviderConfig
if len(setting.SessionConfig.ProviderConfig) == 0 {
setting.SessionConfig.ProviderConfig = "data/sessions"
}
sessionAbsPath, _ = filepath.Abs(setting.SessionConfig.ProviderConfig)
}
if err := zipAddDirectoryExclude(z, "data", setting.AppDataPath, sessionAbsPath); err != nil {
fatal("Failed to include data directory: %v", err)
log.Fatalf("Failed to include data directory: %v", err)
}
}
if err := z.AddDir("log", setting.LogRootPath); err != nil {
fatal("Failed to include log: %v", err)
log.Fatalf("Failed to include log: %v", err)
}
if err = z.Close(); err != nil {
_ = os.Remove(fileName)
fatal("Failed to save %s: %v", fileName, err)
log.Fatalf("Failed to save %s: %v", fileName, err)
}
if err := os.Chmod(fileName, 0600); err != nil {
log.Info("Can't change file access permissions mask to 0600: %v", err)
log.Printf("Can't change file access permissions mask to 0600: %v", err)
}
log.Info("Removing tmp work dir: %s", tmpWorkDir)
log.Printf("Removing tmp work dir: %s", TmpWorkDir)
if err := os.RemoveAll(tmpWorkDir); err != nil {
fatal("Failed to remove %s: %v", tmpWorkDir, err)
if err := os.RemoveAll(TmpWorkDir); err != nil {
log.Fatalf("Failed to remove %s: %v", TmpWorkDir, err)
}
log.Info("Finish dumping in file %s", fileName)
log.Printf("Finish dumping in file %s", fileName)
return nil
}

View File

@@ -1,84 +0,0 @@
// Copyright 2016 The Gogs Authors. All rights reserved.
// Copyright 2016 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 cmd
import (
"fmt"
"code.gitea.io/gitea/modules/generate"
"github.com/urfave/cli"
)
var (
// CmdGenerate represents the available generate sub-command.
CmdGenerate = cli.Command{
Name: "generate",
Usage: "Command line interface for running generators",
Subcommands: []cli.Command{
subcmdSecret,
},
}
subcmdSecret = cli.Command{
Name: "secret",
Usage: "Generate a secret token",
Subcommands: []cli.Command{
microcmdGenerateInternalToken,
microcmdGenerateLfsJwtSecret,
microcmdGenerateSecretKey,
},
}
microcmdGenerateInternalToken = cli.Command{
Name: "INTERNAL_TOKEN",
Usage: "Generate a new INTERNAL_TOKEN",
Action: runGenerateInternalToken,
}
microcmdGenerateLfsJwtSecret = cli.Command{
Name: "JWT_SECRET",
Aliases: []string{"LFS_JWT_SECRET"},
Usage: "Generate a new JWT_SECRET",
Action: runGenerateLfsJwtSecret,
}
microcmdGenerateSecretKey = cli.Command{
Name: "SECRET_KEY",
Usage: "Generate a new SECRET_KEY",
Action: runGenerateSecretKey,
}
)
func runGenerateInternalToken(c *cli.Context) error {
internalToken, err := generate.NewInternalToken()
if err != nil {
return err
}
fmt.Printf("%s\n", internalToken)
return nil
}
func runGenerateLfsJwtSecret(c *cli.Context) error {
JWTSecretBase64, err := generate.NewJwtSecret()
if err != nil {
return err
}
fmt.Printf("%s\n", JWTSecretBase64)
return nil
}
func runGenerateSecretKey(c *cli.Context) error {
secretKey, err := generate.NewSecretKey()
if err != nil {
return err
}
fmt.Printf("%s\n", secretKey)
return nil
}

View File

@@ -7,33 +7,39 @@ package cmd
import (
"bufio"
"bytes"
"crypto/tls"
"fmt"
"net/http"
"os"
"strconv"
"strings"
"code.gitea.io/git"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/Unknwon/com"
"github.com/urfave/cli"
)
const (
hookBatchSize = 30
)
var (
// CmdHook represents the available hooks sub-command.
CmdHook = cli.Command{
Name: "hook",
Usage: "Delegate commands to corresponding Git hooks",
Description: "This should only be called by Git",
Flags: []cli.Flag{
cli.StringFlag{
Name: "config, c",
Value: "custom/conf/app.ini",
Usage: "Custom configuration file path",
},
},
Subcommands: []cli.Command{
subcmdHookPreReceive,
subcmdHookUpdate,
subcmdHookUpadte,
subcmdHookPostReceive,
},
}
@@ -44,7 +50,7 @@ var (
Description: "This command should only be called by Git",
Action: runHookPreReceive,
}
subcmdHookUpdate = cli.Command{
subcmdHookUpadte = cli.Command{
Name: "update",
Usage: "Delegate update Git hook",
Description: "This command should only be called by Git",
@@ -59,49 +65,126 @@ var (
)
func runHookPreReceive(c *cli.Context) error {
if os.Getenv(models.EnvIsInternal) == "true" {
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
return nil
}
setup("hooks/pre-receive.log", false)
if c.IsSet("config") {
setting.CustomConf = c.String("config")
} else if c.GlobalIsSet("config") {
setting.CustomConf = c.GlobalString("config")
}
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
fail(`Rejecting changes as Gitea environment not set.
If you are pushing over SSH you must push with a key managed by
Gitea or set your environment appropriately.`, "")
} else {
return nil
}
if err := setup("hooks/pre-receive.log"); err != nil {
fail("Hook pre-receive init failed", fmt.Sprintf("setup: %v", err))
}
// the environment setted on serv command
repoID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchRepoID), 10, 64)
isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
username := os.Getenv(models.EnvRepoUsername)
reponame := os.Getenv(models.EnvRepoName)
userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
prID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchPRID), 10, 64)
isDeployKey, _ := strconv.ParseBool(os.Getenv(models.EnvIsDeployKey))
//username := os.Getenv(models.EnvRepoUsername)
//reponame := os.Getenv(models.EnvRepoName)
//repoPath := models.RepoPath(username, reponame)
hookOptions := private.HookOptions{
UserID: userID,
GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
ProtectedBranchID: prID,
IsDeployKey: isDeployKey,
buf := bytes.NewBuffer(nil)
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
buf.Write(scanner.Bytes())
buf.WriteByte('\n')
// TODO: support news feeds for wiki
if isWiki {
continue
}
fields := bytes.Fields(scanner.Bytes())
if len(fields) != 3 {
continue
}
//oldCommitID := string(fields[0])
newCommitID := string(fields[1])
refFullName := string(fields[2])
// FIXME: when we add feature to protected branch to deny force push, then uncomment below
/*var isForce bool
// detect force push
if git.EmptySHA != oldCommitID {
output, err := git.NewCommand("rev-list", oldCommitID, "^"+newCommitID).RunInDir(repoPath)
if err != nil {
fail("Internal error", "Fail to detect force push: %v", err)
} else if len(output) > 0 {
isForce = true
}
}*/
branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
protectBranch, err := models.GetProtectedBranchBy(repoID, branchName)
if err != nil {
log.GitLogger.Fatal(2, "retrieve protected branches information failed")
}
if protectBranch != nil {
// check and deletion
if newCommitID == git.EmptySHA {
fail(fmt.Sprintf("branch %s is protected from deletion", branchName), "")
} else {
fail(fmt.Sprintf("protected branch %s can not be pushed to", branchName), "")
//fail(fmt.Sprintf("branch %s is protected from force push", branchName), "")
}
}
}
return nil
}
func runHookUpdate(c *cli.Context) error {
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
return nil
}
if c.IsSet("config") {
setting.CustomConf = c.String("config")
} else if c.GlobalIsSet("config") {
setting.CustomConf = c.GlobalString("config")
}
if err := setup("hooks/update.log"); err != nil {
fail("Hook update init failed", fmt.Sprintf("setup: %v", err))
}
return nil
}
func runHookPostReceive(c *cli.Context) error {
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
return nil
}
if c.IsSet("config") {
setting.CustomConf = c.String("config")
} else if c.GlobalIsSet("config") {
setting.CustomConf = c.GlobalString("config")
}
if err := setup("hooks/post-receive.log"); err != nil {
fail("Hook post-receive init failed", fmt.Sprintf("setup: %v", err))
}
// the environment setted on serv command
repoUser := os.Getenv(models.EnvRepoUsername)
repoUserSalt := os.Getenv(models.EnvRepoUserSalt)
isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
repoName := os.Getenv(models.EnvRepoName)
pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
pusherName := os.Getenv(models.EnvPusherName)
buf := bytes.NewBuffer(nil)
scanner := bufio.NewScanner(os.Stdin)
oldCommitIDs := make([]string, hookBatchSize)
newCommitIDs := make([]string, hookBatchSize)
refFullNames := make([]string, hookBatchSize)
count := 0
total := 0
lastline := 0
for scanner.Scan() {
buf.Write(scanner.Bytes())
buf.WriteByte('\n')
// TODO: support news feeds for wiki
if isWiki {
continue
@@ -115,221 +198,36 @@ Gitea or set your environment appropriately.`, "")
oldCommitID := string(fields[0])
newCommitID := string(fields[1])
refFullName := string(fields[2])
total++
lastline++
// If the ref is a branch, check if it's protected
if strings.HasPrefix(refFullName, git.BranchPrefix) {
oldCommitIDs[count] = oldCommitID
newCommitIDs[count] = newCommitID
refFullNames[count] = refFullName
count++
fmt.Fprintf(os.Stdout, "*")
os.Stdout.Sync()
if err := models.PushUpdate(models.PushUpdateOptions{
RefFullName: refFullName,
OldCommitID: oldCommitID,
NewCommitID: newCommitID,
PusherID: pusherID,
PusherName: pusherName,
RepoUserName: repoUser,
RepoName: repoName,
}); err != nil {
log.GitLogger.Error(2, "Update: %v", err)
}
if count >= hookBatchSize {
fmt.Fprintf(os.Stdout, " Checking %d branches\n", count)
os.Stdout.Sync()
// Ask for running deliver hook and test pull request tasks.
reqURL := setting.LocalURL + repoUser + "/" + repoName + "/tasks/trigger?branch=" +
strings.TrimPrefix(refFullName, git.BranchPrefix) + "&secret=" + base.EncodeMD5(repoUserSalt) + "&pusher=" + com.ToStr(pusherID)
log.GitLogger.Trace("Trigger task: %s", reqURL)
hookOptions.OldCommitIDs = oldCommitIDs
hookOptions.NewCommitIDs = newCommitIDs
hookOptions.RefFullNames = refFullNames
statusCode, msg := private.HookPreReceive(username, reponame, hookOptions)
switch statusCode {
case http.StatusOK:
// no-op
case http.StatusInternalServerError:
fail("Internal Server Error", msg)
default:
fail(msg, "")
}
count = 0
lastline = 0
resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{
InsecureSkipVerify: true,
}).Response()
if err == nil {
resp.Body.Close()
if resp.StatusCode/100 != 2 {
log.GitLogger.Error(2, "Failed to trigger task: not 2xx response code")
}
} else {
fmt.Fprintf(os.Stdout, ".")
os.Stdout.Sync()
}
if lastline >= hookBatchSize {
fmt.Fprintf(os.Stdout, "\n")
os.Stdout.Sync()
lastline = 0
log.GitLogger.Error(2, "Failed to trigger task: %v", err)
}
}
if count > 0 {
hookOptions.OldCommitIDs = oldCommitIDs[:count]
hookOptions.NewCommitIDs = newCommitIDs[:count]
hookOptions.RefFullNames = refFullNames[:count]
fmt.Fprintf(os.Stdout, " Checking %d branches\n", count)
os.Stdout.Sync()
statusCode, msg := private.HookPreReceive(username, reponame, hookOptions)
switch statusCode {
case http.StatusInternalServerError:
fail("Internal Server Error", msg)
case http.StatusForbidden:
fail(msg, "")
}
} else if lastline > 0 {
fmt.Fprintf(os.Stdout, "\n")
os.Stdout.Sync()
lastline = 0
}
fmt.Fprintf(os.Stdout, "Checked %d references in total\n", total)
os.Stdout.Sync()
return nil
}
func runHookUpdate(c *cli.Context) error {
// Update is empty and is kept only for backwards compatibility
return nil
}
func runHookPostReceive(c *cli.Context) error {
if os.Getenv(models.EnvIsInternal) == "true" {
return nil
}
setup("hooks/post-receive.log", false)
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
fail(`Rejecting changes as Gitea environment not set.
If you are pushing over SSH you must push with a key managed by
Gitea or set your environment appropriately.`, "")
} else {
return nil
}
}
// the environment setted on serv command
repoUser := os.Getenv(models.EnvRepoUsername)
isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
repoName := os.Getenv(models.EnvRepoName)
pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
pusherName := os.Getenv(models.EnvPusherName)
hookOptions := private.HookOptions{
UserName: pusherName,
UserID: pusherID,
GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
}
oldCommitIDs := make([]string, hookBatchSize)
newCommitIDs := make([]string, hookBatchSize)
refFullNames := make([]string, hookBatchSize)
count := 0
total := 0
wasEmpty := false
masterPushed := false
results := make([]private.HookPostReceiveBranchResult, 0)
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
// TODO: support news feeds for wiki
if isWiki {
continue
}
fields := bytes.Fields(scanner.Bytes())
if len(fields) != 3 {
continue
}
fmt.Fprintf(os.Stdout, ".")
oldCommitIDs[count] = string(fields[0])
newCommitIDs[count] = string(fields[1])
refFullNames[count] = string(fields[2])
if refFullNames[count] == git.BranchPrefix+"master" && newCommitIDs[count] != git.EmptySHA && count == total {
masterPushed = true
}
count++
total++
os.Stdout.Sync()
if count >= hookBatchSize {
fmt.Fprintf(os.Stdout, " Processing %d references\n", count)
os.Stdout.Sync()
hookOptions.OldCommitIDs = oldCommitIDs
hookOptions.NewCommitIDs = newCommitIDs
hookOptions.RefFullNames = refFullNames
resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
if resp == nil {
hookPrintResults(results)
fail("Internal Server Error", err)
}
wasEmpty = wasEmpty || resp.RepoWasEmpty
results = append(results, resp.Results...)
count = 0
}
}
if count == 0 {
if wasEmpty && masterPushed {
// We need to tell the repo to reset the default branch to master
err := private.SetDefaultBranch(repoUser, repoName, "master")
if err != nil {
fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
}
}
fmt.Fprintf(os.Stdout, "Processed %d references in total\n", total)
os.Stdout.Sync()
hookPrintResults(results)
return nil
}
hookOptions.OldCommitIDs = oldCommitIDs[:count]
hookOptions.NewCommitIDs = newCommitIDs[:count]
hookOptions.RefFullNames = refFullNames[:count]
fmt.Fprintf(os.Stdout, " Processing %d references\n", count)
os.Stdout.Sync()
resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
if resp == nil {
hookPrintResults(results)
fail("Internal Server Error", err)
}
wasEmpty = wasEmpty || resp.RepoWasEmpty
results = append(results, resp.Results...)
fmt.Fprintf(os.Stdout, "Processed %d references in total\n", total)
os.Stdout.Sync()
if wasEmpty && masterPushed {
// We need to tell the repo to reset the default branch to master
err := private.SetDefaultBranch(repoUser, repoName, "master")
if err != nil {
fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
}
}
hookPrintResults(results)
return nil
}
func hookPrintResults(results []private.HookPostReceiveBranchResult) {
for _, res := range results {
if !res.Message {
continue
}
fmt.Fprintln(os.Stderr, "")
if res.Create {
fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", res.Branch)
fmt.Fprintf(os.Stderr, " %s\n", res.URL)
} else {
fmt.Fprint(os.Stderr, "Visit the existing pull request:\n")
fmt.Fprintf(os.Stderr, " %s\n", res.URL)
}
fmt.Fprintln(os.Stderr, "")
os.Stderr.Sync()
}
}

View File

@@ -1,73 +0,0 @@
// Copyright 2018 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 cmd
import (
"errors"
"fmt"
"strings"
"code.gitea.io/gitea/modules/private"
"github.com/urfave/cli"
)
// CmdKeys represents the available keys sub-command
var CmdKeys = cli.Command{
Name: "keys",
Usage: "This command queries the Gitea database to get the authorized command for a given ssh key fingerprint",
Action: runKeys,
Flags: []cli.Flag{
cli.StringFlag{
Name: "expected, e",
Value: "git",
Usage: "Expected user for whom provide key commands",
},
cli.StringFlag{
Name: "username, u",
Value: "",
Usage: "Username trying to log in by SSH",
},
cli.StringFlag{
Name: "type, t",
Value: "",
Usage: "Type of the SSH key provided to the SSH Server (requires content to be provided too)",
},
cli.StringFlag{
Name: "content, k",
Value: "",
Usage: "Base64 encoded content of the SSH key provided to the SSH Server (requires type to be provided too)",
},
},
}
func runKeys(c *cli.Context) error {
if !c.IsSet("username") {
return errors.New("No username provided")
}
// Check username matches the expected username
if strings.TrimSpace(c.String("username")) != strings.TrimSpace(c.String("expected")) {
return nil
}
content := ""
if c.IsSet("type") && c.IsSet("content") {
content = fmt.Sprintf("%s %s", strings.TrimSpace(c.String("type")), strings.TrimSpace(c.String("content")))
}
if content == "" {
return errors.New("No key type and content provided")
}
setup("keys.log", false)
authorizedString, err := private.AuthorizedPublicKeyByContent(content)
if err != nil {
return err
}
fmt.Println(strings.TrimSpace(authorizedString))
return nil
}

View File

@@ -1,43 +0,0 @@
// Copyright 2018 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 cmd
import (
"context"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/migrations"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli"
)
// CmdMigrate represents the available migrate sub-command.
var CmdMigrate = cli.Command{
Name: "migrate",
Usage: "Migrate the database",
Description: "This is a command for migrating the database, so that you can run gitea admin create-user before starting the server.",
Action: runMigrate,
}
func runMigrate(ctx *cli.Context) error {
if err := initDB(); err != nil {
return err
}
log.Trace("AppPath: %s", setting.AppPath)
log.Trace("AppWorkPath: %s", setting.AppWorkPath)
log.Trace("Custom path: %s", setting.CustomPath)
log.Trace("Log path: %s", setting.LogRootPath)
setting.InitDBConfig()
if err := models.NewEngine(context.Background(), migrations.Migrate); err != nil {
log.Fatal("Failed to initialize ORM engine: %v", err)
return err
}
return nil
}

View File

@@ -8,26 +8,22 @@ package cmd
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"os/exec"
"strconv"
"path/filepath"
"strings"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/pprof"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
"github.com/Unknwon/com"
"github.com/dgrijalva/jwt-go"
"github.com/unknwon/com"
"github.com/urfave/cli"
)
const (
accessDenied = "Repository does not exist or you do not have access"
lfsAuthenticateVerb = "git-lfs-authenticate"
)
@@ -38,23 +34,28 @@ var CmdServ = cli.Command{
Description: `Serv provide access auth for repositories`,
Action: runServ,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "enable-pprof",
},
cli.BoolFlag{
Name: "debug",
cli.StringFlag{
Name: "config, c",
Value: "custom/conf/app.ini",
Usage: "Custom configuration file path",
},
},
}
func setup(logPath string, debug bool) {
if !debug {
_ = log.DelLogger("console")
}
func setup(logPath string) error {
setting.NewContext()
if debug {
setting.ProdMode = false
log.NewGitLogger(filepath.Join(setting.LogRootPath, logPath))
models.LoadConfigs()
if setting.UseSQLite3 || setting.UseTiDB {
workDir, _ := setting.WorkDir()
if err := os.Chdir(workDir); err != nil {
log.GitLogger.Fatal(4, "Failed to change directory %s: %v", workDir, err)
}
}
setting.NewXORMLogService(true)
return models.SetEngine()
}
func parseCmd(cmd string) (string, string) {
@@ -81,14 +82,22 @@ func fail(userMessage, logMessage string, args ...interface{}) {
if !setting.ProdMode {
fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
}
log.GitLogger.Fatal(3, logMessage, args...)
return
}
log.GitLogger.Close()
os.Exit(1)
}
func runServ(c *cli.Context) error {
// FIXME: This needs to internationalised
setup("serv.log", c.Bool("debug"))
if c.IsSet("config") {
setting.CustomConf = c.String("config")
}
if err := setup("serv.log"); err != nil {
fail("System init failed", fmt.Sprintf("setup: %v", err))
}
if setting.SSH.Disabled {
println("Gitea: SSH has been disabled")
@@ -96,29 +105,12 @@ func runServ(c *cli.Context) error {
}
if len(c.Args()) < 1 {
if err := cli.ShowSubcommandHelp(c); err != nil {
fmt.Printf("error showing subcommand help: %v\n", err)
}
return nil
fail("Not enough arguments", "Not enough arguments")
}
keys := strings.Split(c.Args()[0], "-")
if len(keys) != 2 || keys[0] != "key" {
fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
}
keyID := com.StrTo(keys[1]).MustInt64()
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
if len(cmd) == 0 {
key, user, err := private.ServNoCommand(keyID)
if err != nil {
fail("Internal error", "Failed to check provided key: %v", err)
}
if key.Type == models.KeyTypeDeploy {
println("Hi there! You've successfully authenticated with the deploy key named " + key.Name + ", but Gitea does not provide shell access.")
} else {
println("Hi there, " + user.Name + "! You've successfully authenticated with the key named " + key.Name + ", but Gitea does not provide shell access.")
}
println("Hi there, You've successfully authenticated, but Gitea does not provide shell access.")
println("If this is unexpected, please log in with password and setup Gitea under another user.")
return nil
}
@@ -131,8 +123,8 @@ func runServ(c *cli.Context) error {
fail("Unknown git command", "LFS authentication request over SSH denied, LFS support is disabled")
}
argsSplit := strings.Split(args, " ")
if len(argsSplit) >= 2 {
if strings.Contains(args, " ") {
argsSplit := strings.SplitN(args, " ", 2)
args = strings.TrimSpace(argsSplit[0])
lfsVerb = strings.TrimSpace(argsSplit[1])
}
@@ -147,22 +139,36 @@ func runServ(c *cli.Context) error {
username := strings.ToLower(rr[0])
reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
if setting.EnablePprof || c.Bool("enable-pprof") {
if err := os.MkdirAll(setting.PprofDataPath, os.ModePerm); err != nil {
fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err)
}
isWiki := false
if strings.HasSuffix(reponame, ".wiki") {
isWiki = true
reponame = reponame[:len(reponame)-5]
}
stopCPUProfiler, err := pprof.DumpCPUProfileForUsername(setting.PprofDataPath, username)
if err != nil {
fail("Internal Server Error", "Unable to start CPU profile: %v", err)
os.Setenv(models.EnvRepoUsername, username)
if isWiki {
os.Setenv(models.EnvRepoIsWiki, "true")
} else {
os.Setenv(models.EnvRepoIsWiki, "false")
}
os.Setenv(models.EnvRepoName, reponame)
repoUser, err := models.GetUserByName(username)
if err != nil {
if models.IsErrUserNotExist(err) {
fail("Repository owner does not exist", "Unregistered owner: %s", username)
}
defer func() {
stopCPUProfiler()
err := pprof.DumpMemProfileForUsername(setting.PprofDataPath, username)
if err != nil {
fail("Internal Server Error", "Unable to dump Mem Profile: %v", err)
}
}()
fail("Internal error", "Failed to get repository owner (%s): %v", username, err)
}
os.Setenv(models.EnvRepoUserSalt, repoUser.Salt)
repo, err := models.GetRepositoryByName(repoUser.ID, reponame)
if err != nil {
if models.IsErrRepoNotExist(err) {
fail(accessDenied, "Repository does not exist: %s/%s", repoUser.Name, reponame)
}
fail("Internal error", "Failed to get repository: %v", err)
}
requestedMode, has := allowedCommands[verb]
@@ -173,48 +179,88 @@ func runServ(c *cli.Context) error {
if verb == lfsAuthenticateVerb {
if lfsVerb == "upload" {
requestedMode = models.AccessModeWrite
} else if lfsVerb == "download" {
requestedMode = models.AccessModeRead
} else {
fail("Unknown LFS verb", "Unknown lfs verb %s", lfsVerb)
requestedMode = models.AccessModeRead
}
}
results, err := private.ServCommand(keyID, username, reponame, requestedMode, verb, lfsVerb)
if err != nil {
if private.IsErrServCommand(err) {
errServCommand := err.(private.ErrServCommand)
if errServCommand.StatusCode != http.StatusInternalServerError {
fail("Unauthorized", "%s", errServCommand.Error())
} else {
fail("Internal Server Error", "%s", errServCommand.Error())
}
}
fail("Internal Server Error", "%s", err.Error())
// Prohibit push to mirror repositories.
if requestedMode > models.AccessModeRead && repo.IsMirror {
fail("mirror repository is read-only", "")
}
// Allow anonymous clone for public repositories.
var (
keyID int64
user *models.User
)
if requestedMode == models.AccessModeWrite || repo.IsPrivate {
keys := strings.Split(c.Args()[0], "-")
if len(keys) != 2 {
fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
}
key, err := models.GetPublicKeyByID(com.StrTo(keys[1]).MustInt64())
if err != nil {
fail("Invalid key ID", "Invalid key ID[%s]: %v", c.Args()[0], err)
}
keyID = key.ID
// Check deploy key or user key.
if key.Type == models.KeyTypeDeploy {
if key.Mode < requestedMode {
fail("Key permission denied", "Cannot push with deployment key: %d", key.ID)
}
// Check if this deploy key belongs to current repository.
if !models.HasDeployKey(key.ID, repo.ID) {
fail("Key access denied", "Deploy key access denied: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
}
// Update deploy key activity.
deployKey, err := models.GetDeployKeyByRepo(key.ID, repo.ID)
if err != nil {
fail("Internal error", "GetDeployKey: %v", err)
}
deployKey.Updated = time.Now()
if err = models.UpdateDeployKey(deployKey); err != nil {
fail("Internal error", "UpdateDeployKey: %v", err)
}
} else {
user, err = models.GetUserByKeyID(key.ID)
if err != nil {
fail("internal error", "Failed to get user by key ID(%d): %v", keyID, err)
}
mode, err := models.AccessLevel(user, repo)
if err != nil {
fail("Internal error", "Failed to check access: %v", err)
} else if mode < requestedMode {
clientMessage := accessDenied
if mode >= models.AccessModeRead {
clientMessage = "You do not have sufficient authorization for this action"
}
fail(clientMessage,
"User %s does not have level %v access to repository %s",
user.Name, requestedMode, repoPath)
}
os.Setenv(models.EnvPusherName, user.Name)
os.Setenv(models.EnvPusherID, fmt.Sprintf("%d", user.ID))
}
}
os.Setenv(models.EnvRepoIsWiki, strconv.FormatBool(results.IsWiki))
os.Setenv(models.EnvRepoName, results.RepoName)
os.Setenv(models.EnvRepoUsername, results.OwnerName)
os.Setenv(models.EnvPusherName, results.UserName)
os.Setenv(models.EnvPusherID, strconv.FormatInt(results.UserID, 10))
os.Setenv(models.ProtectedBranchRepoID, strconv.FormatInt(results.RepoID, 10))
os.Setenv(models.ProtectedBranchPRID, fmt.Sprintf("%d", 0))
os.Setenv(models.EnvIsDeployKey, fmt.Sprintf("%t", results.IsDeployKey))
os.Setenv(models.EnvKeyID, fmt.Sprintf("%d", results.KeyID))
//LFS token authentication
if verb == lfsAuthenticateVerb {
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, repoUser.Name, repo.Name)
now := time.Now()
claims := jwt.MapClaims{
"repo": results.RepoID,
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"repo": repo.ID,
"op": lfsVerb,
"exp": now.Add(setting.LFS.HTTPAuthExpiry).Unix(),
"exp": now.Add(5 * time.Minute).Unix(),
"nbf": now.Unix(),
"user": results.UserID,
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
})
// Sign and get the complete encoded token as a string using the secret
tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes)
@@ -233,6 +279,7 @@ func runServ(c *cli.Context) error {
if err != nil {
fail("Internal error", "Failed to encode LFS json response: %v", err)
}
return nil
}
@@ -249,6 +296,8 @@ func runServ(c *cli.Context) error {
gitcmd = exec.Command(verb, repoPath)
}
os.Setenv(models.ProtectedBranchRepoID, fmt.Sprintf("%d", repo.ID))
gitcmd.Dir = setting.RepoRootPath
gitcmd.Stdout = os.Stdout
gitcmd.Stdin = os.Stdin
@@ -258,9 +307,15 @@ func runServ(c *cli.Context) error {
}
// Update user key activity.
if results.KeyID > 0 {
if err = private.UpdatePublicKeyInRepo(results.KeyID, results.RepoID); err != nil {
fail("Internal error", "UpdatePublicKeyInRepo: %v", err)
if keyID > 0 {
key, err := models.GetPublicKeyByID(keyID)
if err != nil {
fail("Internal error", "GetPublicKeyById: %v", err)
}
key.Updated = time.Now()
if err = models.UpdatePublicKey(key); err != nil {
fail("Internal error", "UpdatePublicKey: %v", err)
}
}

View File

@@ -5,24 +5,43 @@
package cmd
import (
"context"
"fmt"
"net"
"net/http"
"net/http/fcgi"
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
"os"
"path"
"strings"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/options"
"code.gitea.io/gitea/modules/public"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/routers"
"code.gitea.io/gitea/routers/routes"
"code.gitea.io/gitea/routers/admin"
apiv1 "code.gitea.io/gitea/routers/api/v1"
"code.gitea.io/gitea/routers/dev"
"code.gitea.io/gitea/routers/org"
"code.gitea.io/gitea/routers/repo"
"code.gitea.io/gitea/routers/user"
"github.com/go-macaron/binding"
"github.com/go-macaron/cache"
"github.com/go-macaron/captcha"
"github.com/go-macaron/csrf"
"github.com/go-macaron/gzip"
"github.com/go-macaron/i18n"
"github.com/go-macaron/session"
"github.com/go-macaron/toolbox"
context2 "github.com/gorilla/context"
"github.com/unknwon/com"
"github.com/urfave/cli"
"golang.org/x/crypto/acme/autocert"
ini "gopkg.in/ini.v1"
macaron "gopkg.in/macaron.v1"
)
// CmdWeb represents the available web sub-command.
@@ -38,6 +57,11 @@ and it takes care of all the other things for you`,
Value: "3000",
Usage: "Temporary port number to prevent conflict",
},
cli.StringFlag{
Name: "config, c",
Value: "custom/conf/app.ini",
Usage: "Custom configuration file path",
},
cli.StringFlag{
Name: "pid, P",
Value: "/var/run/gitea.pid",
@@ -46,117 +70,593 @@ and it takes care of all the other things for you`,
},
}
func runHTTPRedirector() {
source := fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.PortToRedirect)
dest := strings.TrimSuffix(setting.AppURL, "/")
log.Info("Redirecting: %s to %s", source, dest)
// newMacaron initializes Macaron instance.
func newMacaron() *macaron.Macaron {
m := macaron.New()
if !setting.DisableRouterLog {
m.Use(macaron.Logger())
}
m.Use(macaron.Recovery())
if setting.EnableGzip {
m.Use(gzip.Gziper())
}
if setting.Protocol == setting.FCGI {
m.SetURLPrefix(setting.AppSubURL)
}
m.Use(public.Custom(
&public.Options{
SkipLogging: setting.DisableRouterLog,
},
))
m.Use(public.Static(
&public.Options{
Directory: path.Join(setting.StaticRootPath, "public"),
SkipLogging: setting.DisableRouterLog,
},
))
m.Use(macaron.Static(
setting.AvatarUploadPath,
macaron.StaticOptions{
Prefix: "avatars",
SkipLogging: setting.DisableRouterLog,
ETag: true,
},
))
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
target := dest + r.URL.Path
if len(r.URL.RawQuery) > 0 {
target += "?" + r.URL.RawQuery
}
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
})
m.Use(templates.Renderer())
models.InitMailRender(templates.Mailer())
var err = runHTTP("tcp", source, context2.ClearHandler(handler))
localeNames, err := options.Dir("locale")
if err != nil {
log.Fatal("Failed to start port redirection: %v", err)
log.Fatal(4, "Failed to list locale files: %v", err)
}
}
func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler) error {
certManager := autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist(domain),
Cache: autocert.DirCache(directory),
Email: email,
}
go func() {
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)
var err = runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, certManager.HTTPHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)))
localFiles := make(map[string][]byte)
for _, name := range localeNames {
localFiles[name], err = options.Locale(name)
if err != nil {
log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
log.Fatal(4, "Failed to load %s locale file. %v", name, err)
}
}()
return runHTTPSWithTLSConfig("tcp", listenAddr, certManager.TLSConfig(), context2.ClearHandler(m))
}
func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" && r.Method != "HEAD" {
http.Error(w, "Use HTTPS", http.StatusBadRequest)
return
}
// Remove the trailing slash at the end of setting.AppURL, the request
// URI always contains a leading slash, which would result in a double
// slash
target := strings.TrimRight(setting.AppURL, "/") + r.URL.RequestURI()
http.Redirect(w, r, target, http.StatusFound)
m.Use(i18n.I18n(i18n.Options{
SubURL: setting.AppSubURL,
Files: localFiles,
Langs: setting.Langs,
Names: setting.Names,
DefaultLang: "en-US",
Redirect: true,
}))
m.Use(cache.Cacher(cache.Options{
Adapter: setting.CacheAdapter,
AdapterConfig: setting.CacheConn,
Interval: setting.CacheInterval,
}))
m.Use(captcha.Captchaer(captcha.Options{
SubURL: setting.AppSubURL,
}))
m.Use(session.Sessioner(setting.SessionConfig))
m.Use(csrf.Csrfer(csrf.Options{
Secret: setting.SecretKey,
Cookie: setting.CSRFCookieName,
SetCookie: true,
Header: "X-Csrf-Token",
CookiePath: setting.AppSubURL,
}))
m.Use(toolbox.Toolboxer(m, toolbox.Options{
HealthCheckFuncs: []*toolbox.HealthCheckFuncDesc{
{
Desc: "Database connection",
Func: models.Ping,
},
},
}))
m.Use(context.Contexter())
return m
}
func runWeb(ctx *cli.Context) error {
managerCtx, cancel := context.WithCancel(context.Background())
graceful.InitManager(managerCtx)
defer cancel()
if os.Getppid() > 1 && len(os.Getenv("LISTEN_FDS")) > 0 {
log.Info("Restarting Gitea on PID: %d from parent PID: %d", os.Getpid(), os.Getppid())
} else {
log.Info("Starting Gitea on PID: %d", os.Getpid())
if ctx.IsSet("config") {
setting.CustomConf = ctx.String("config")
}
// Set pid file setting
if ctx.IsSet("pid") {
setting.CustomPID = ctx.String("pid")
}
// Perform global initialization
routers.GlobalInit(graceful.GetManager().HammerContext())
routers.GlobalInit()
// Set up Macaron
m := routes.NewMacaron()
routes.RegisterRoutes(m)
m := newMacaron()
reqSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: true})
ignSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: setting.Service.RequireSignInView})
ignSignInAndCsrf := context.Toggle(&context.ToggleOptions{DisableCSRF: true})
reqSignOut := context.Toggle(&context.ToggleOptions{SignOutRequired: true})
bindIgnErr := binding.BindIgnErr
m.Use(user.GetNotificationCount)
// FIXME: not all routes need go through same middlewares.
// Especially some AJAX requests, we can reduce middleware number to improve performance.
// Routers.
m.Get("/", ignSignIn, routers.Home)
m.Group("/explore", func() {
m.Get("", func(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/explore/repos")
})
m.Get("/repos", routers.ExploreRepos)
m.Get("/users", routers.ExploreUsers)
m.Get("/organizations", routers.ExploreOrganizations)
}, ignSignIn)
m.Combo("/install", routers.InstallInit).Get(routers.Install).
Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost)
m.Get("/^:type(issues|pulls)$", reqSignIn, user.Issues)
// ***** START: User *****
m.Group("/user", func() {
m.Get("/login", user.SignIn)
m.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost)
m.Get("/sign_up", user.SignUp)
m.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost)
m.Get("/reset_password", user.ResetPasswd)
m.Post("/reset_password", user.ResetPasswdPost)
m.Group("/oauth2", func() {
m.Get("/:provider", user.SignInOAuth)
m.Get("/:provider/callback", user.SignInOAuthCallback)
})
m.Get("/link_account", user.LinkAccount)
m.Post("/link_account_signin", bindIgnErr(auth.SignInForm{}), user.LinkAccountPostSignIn)
m.Post("/link_account_signup", bindIgnErr(auth.RegisterForm{}), user.LinkAccountPostRegister)
m.Group("/two_factor", func() {
m.Get("", user.TwoFactor)
m.Post("", bindIgnErr(auth.TwoFactorAuthForm{}), user.TwoFactorPost)
m.Get("/scratch", user.TwoFactorScratch)
m.Post("/scratch", bindIgnErr(auth.TwoFactorScratchAuthForm{}), user.TwoFactorScratchPost)
})
}, reqSignOut)
m.Group("/user/settings", func() {
m.Get("", user.Settings)
m.Post("", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost)
m.Combo("/avatar").Get(user.SettingsAvatar).
Post(binding.MultipartForm(auth.AvatarForm{}), user.SettingsAvatarPost)
m.Post("/avatar/delete", user.SettingsDeleteAvatar)
m.Combo("/email").Get(user.SettingsEmails).
Post(bindIgnErr(auth.AddEmailForm{}), user.SettingsEmailPost)
m.Post("/email/delete", user.DeleteEmail)
m.Get("/password", user.SettingsPassword)
m.Post("/password", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsPasswordPost)
m.Combo("/ssh").Get(user.SettingsSSHKeys).
Post(bindIgnErr(auth.AddSSHKeyForm{}), user.SettingsSSHKeysPost)
m.Post("/ssh/delete", user.DeleteSSHKey)
m.Combo("/applications").Get(user.SettingsApplications).
Post(bindIgnErr(auth.NewAccessTokenForm{}), user.SettingsApplicationsPost)
m.Post("/applications/delete", user.SettingsDeleteApplication)
m.Route("/delete", "GET,POST", user.SettingsDelete)
m.Combo("/account_link").Get(user.SettingsAccountLinks).Post(user.SettingsDeleteAccountLink)
m.Group("/two_factor", func() {
m.Get("", user.SettingsTwoFactor)
m.Post("/regenerate_scratch", user.SettingsTwoFactorRegenerateScratch)
m.Post("/disable", user.SettingsTwoFactorDisable)
m.Get("/enroll", user.SettingsTwoFactorEnroll)
m.Post("/enroll", bindIgnErr(auth.TwoFactorAuthForm{}), user.SettingsTwoFactorEnrollPost)
})
}, reqSignIn, func(ctx *context.Context) {
ctx.Data["PageIsUserSettings"] = true
})
m.Group("/user", func() {
// r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds)
m.Any("/activate", user.Activate)
m.Any("/activate_email", user.ActivateEmail)
m.Get("/email2user", user.Email2User)
m.Get("/forget_password", user.ForgotPasswd)
m.Post("/forget_password", user.ForgotPasswdPost)
m.Get("/logout", user.SignOut)
})
// ***** END: User *****
adminReq := context.Toggle(&context.ToggleOptions{SignInRequired: true, AdminRequired: true})
// ***** START: Admin *****
m.Group("/admin", func() {
m.Get("", adminReq, admin.Dashboard)
m.Get("/config", admin.Config)
m.Post("/config/test_mail", admin.SendTestMail)
m.Get("/monitor", admin.Monitor)
m.Group("/users", func() {
m.Get("", admin.Users)
m.Combo("/new").Get(admin.NewUser).Post(bindIgnErr(auth.AdminCreateUserForm{}), admin.NewUserPost)
m.Combo("/:userid").Get(admin.EditUser).Post(bindIgnErr(auth.AdminEditUserForm{}), admin.EditUserPost)
m.Post("/:userid/delete", admin.DeleteUser)
})
m.Group("/orgs", func() {
m.Get("", admin.Organizations)
})
m.Group("/repos", func() {
m.Get("", admin.Repos)
m.Post("/delete", admin.DeleteRepo)
})
m.Group("/auths", func() {
m.Get("", admin.Authentications)
m.Combo("/new").Get(admin.NewAuthSource).Post(bindIgnErr(auth.AuthenticationForm{}), admin.NewAuthSourcePost)
m.Combo("/:authid").Get(admin.EditAuthSource).
Post(bindIgnErr(auth.AuthenticationForm{}), admin.EditAuthSourcePost)
m.Post("/:authid/delete", admin.DeleteAuthSource)
})
m.Group("/notices", func() {
m.Get("", admin.Notices)
m.Post("/delete", admin.DeleteNotices)
m.Get("/empty", admin.EmptyNotices)
})
}, adminReq)
// ***** END: Admin *****
m.Group("", func() {
m.Group("/:username", func() {
m.Get("", user.Profile)
m.Get("/followers", user.Followers)
m.Get("/following", user.Following)
})
m.Get("/attachments/:uuid", func(ctx *context.Context) {
attach, err := models.GetAttachmentByUUID(ctx.Params(":uuid"))
if err != nil {
if models.IsErrAttachmentNotExist(err) {
ctx.Error(404)
} else {
ctx.Handle(500, "GetAttachmentByUUID", err)
}
return
}
fr, err := os.Open(attach.LocalPath())
if err != nil {
ctx.Handle(500, "Open", err)
return
}
defer fr.Close()
if err = repo.ServeData(ctx, attach.Name, fr); err != nil {
ctx.Handle(500, "ServeData", err)
return
}
})
m.Post("/attachments", repo.UploadAttachment)
}, ignSignIn)
m.Group("/:username", func() {
m.Get("/action/:action", user.Action)
}, reqSignIn)
if macaron.Env == macaron.DEV {
m.Get("/template/*", dev.TemplatePreview)
}
reqRepoAdmin := context.RequireRepoAdmin()
reqRepoWriter := context.RequireRepoWriter()
// ***** START: Organization *****
m.Group("/org", func() {
m.Group("", func() {
m.Get("/create", org.Create)
m.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.CreatePost)
}, func(ctx *context.Context) {
if !ctx.User.CanCreateOrganization() {
ctx.NotFound()
}
})
m.Group("/:org", func() {
m.Get("/dashboard", user.Dashboard)
m.Get("/^:type(issues|pulls)$", user.Issues)
m.Get("/members", org.Members)
m.Get("/members/action/:action", org.MembersAction)
m.Get("/teams", org.Teams)
}, context.OrgAssignment(true))
m.Group("/:org", func() {
m.Get("/teams/:team", org.TeamMembers)
m.Get("/teams/:team/repositories", org.TeamRepositories)
m.Route("/teams/:team/action/:action", "GET,POST", org.TeamsAction)
m.Route("/teams/:team/action/repo/:action", "GET,POST", org.TeamsRepoAction)
}, context.OrgAssignment(true, false, true))
m.Group("/:org", func() {
m.Get("/teams/new", org.NewTeam)
m.Post("/teams/new", bindIgnErr(auth.CreateTeamForm{}), org.NewTeamPost)
m.Get("/teams/:team/edit", org.EditTeam)
m.Post("/teams/:team/edit", bindIgnErr(auth.CreateTeamForm{}), org.EditTeamPost)
m.Post("/teams/:team/delete", org.DeleteTeam)
m.Group("/settings", func() {
m.Combo("").Get(org.Settings).
Post(bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost)
m.Post("/avatar", binding.MultipartForm(auth.AvatarForm{}), org.SettingsAvatar)
m.Post("/avatar/delete", org.SettingsDeleteAvatar)
m.Group("/hooks", func() {
m.Get("", org.Webhooks)
m.Post("/delete", org.DeleteWebhook)
m.Get("/:type/new", repo.WebhooksNew)
m.Post("/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
m.Get("/:id", repo.WebHooksEdit)
m.Post("/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
})
m.Route("/delete", "GET,POST", org.SettingsDelete)
})
m.Route("/invitations/new", "GET,POST", org.Invitation)
}, context.OrgAssignment(true, true))
}, reqSignIn)
// ***** END: Organization *****
// ***** START: Repository *****
m.Group("/repo", func() {
m.Get("/create", repo.Create)
m.Post("/create", bindIgnErr(auth.CreateRepoForm{}), repo.CreatePost)
m.Get("/migrate", repo.Migrate)
m.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), repo.MigratePost)
m.Combo("/fork/:repoid").Get(repo.Fork).
Post(bindIgnErr(auth.CreateRepoForm{}), repo.ForkPost)
}, reqSignIn)
m.Group("/:username/:reponame", func() {
m.Group("/settings", func() {
m.Combo("").Get(repo.Settings).
Post(bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost)
m.Group("/collaboration", func() {
m.Combo("").Get(repo.Collaboration).Post(repo.CollaborationPost)
m.Post("/access_mode", repo.ChangeCollaborationAccessMode)
m.Post("/delete", repo.DeleteCollaboration)
})
m.Group("/branches", func() {
m.Combo("").Get(repo.ProtectedBranch).Post(repo.ProtectedBranchPost)
m.Post("/can_push", repo.ChangeProtectedBranch)
m.Post("/delete", repo.DeleteProtectedBranch)
})
m.Group("/hooks", func() {
m.Get("", repo.Webhooks)
m.Post("/delete", repo.DeleteWebhook)
m.Get("/:type/new", repo.WebhooksNew)
m.Post("/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
m.Get("/:id", repo.WebHooksEdit)
m.Post("/:id/test", repo.TestWebhook)
m.Post("/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
m.Group("/git", func() {
m.Get("", repo.GitHooks)
m.Combo("/:name").Get(repo.GitHooksEdit).
Post(repo.GitHooksEditPost)
}, context.GitHookService())
})
m.Group("/keys", func() {
m.Combo("").Get(repo.DeployKeys).
Post(bindIgnErr(auth.AddSSHKeyForm{}), repo.DeployKeysPost)
m.Post("/delete", repo.DeleteDeployKey)
})
}, func(ctx *context.Context) {
ctx.Data["PageIsSettings"] = true
}, context.UnitTypes())
}, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.RepoRef())
m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), repo.Action)
m.Group("/:username/:reponame", func() {
// FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest.
// So they can apply their own enable/disable logic on routers.
m.Group("/issues", func() {
m.Combo("/new", repo.MustEnableIssues).Get(context.RepoRef(), repo.NewIssue).
Post(bindIgnErr(auth.CreateIssueForm{}), repo.NewIssuePost)
m.Group("/:index", func() {
m.Post("/label", repo.UpdateIssueLabel)
m.Post("/milestone", repo.UpdateIssueMilestone)
m.Post("/assignee", repo.UpdateIssueAssignee)
}, reqRepoWriter)
m.Group("/:index", func() {
m.Post("/title", repo.UpdateIssueTitle)
m.Post("/content", repo.UpdateIssueContent)
m.Combo("/comments").Post(bindIgnErr(auth.CreateCommentForm{}), repo.NewComment)
})
})
m.Group("/comments/:id", func() {
m.Post("", repo.UpdateCommentContent)
m.Post("/delete", repo.DeleteComment)
})
m.Group("/labels", func() {
m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel)
m.Post("/edit", bindIgnErr(auth.CreateLabelForm{}), repo.UpdateLabel)
m.Post("/delete", repo.DeleteLabel)
m.Post("/initialize", bindIgnErr(auth.InitializeLabelsForm{}), repo.InitializeLabels)
}, reqRepoWriter, context.RepoRef())
m.Group("/milestones", func() {
m.Combo("/new").Get(repo.NewMilestone).
Post(bindIgnErr(auth.CreateMilestoneForm{}), repo.NewMilestonePost)
m.Get("/:id/edit", repo.EditMilestone)
m.Post("/:id/edit", bindIgnErr(auth.CreateMilestoneForm{}), repo.EditMilestonePost)
m.Get("/:id/:action", repo.ChangeMilestonStatus)
m.Post("/delete", repo.DeleteMilestone)
}, reqRepoWriter, context.RepoRef())
m.Group("/releases", func() {
m.Get("/new", repo.NewRelease)
m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
m.Post("/delete", repo.DeleteRelease)
}, reqRepoWriter, context.RepoRef())
m.Group("/releases", func() {
m.Get("/edit/*", repo.EditRelease)
m.Post("/edit/*", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
}, reqRepoWriter, func(ctx *context.Context) {
var err error
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
if err != nil {
ctx.Handle(500, "GetBranchCommit", err)
return
}
ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount()
if err != nil {
ctx.Handle(500, "CommitsCount", err)
return
}
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
})
m.Combo("/compare/*", repo.MustAllowPulls, repo.SetEditorconfigIfExists).
Get(repo.CompareAndPullRequest).
Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
m.Group("", func() {
m.Combo("/_edit/*").Get(repo.EditFile).
Post(bindIgnErr(auth.EditRepoFileForm{}), repo.EditFilePost)
m.Combo("/_new/*").Get(repo.NewFile).
Post(bindIgnErr(auth.EditRepoFileForm{}), repo.NewFilePost)
m.Post("/_preview/*", bindIgnErr(auth.EditPreviewDiffForm{}), repo.DiffPreviewPost)
m.Combo("/_delete/*").Get(repo.DeleteFile).
Post(bindIgnErr(auth.DeleteRepoFileForm{}), repo.DeleteFilePost)
m.Group("", func() {
m.Combo("/_upload/*").Get(repo.UploadFile).
Post(bindIgnErr(auth.UploadRepoFileForm{}), repo.UploadFilePost)
m.Post("/upload-file", repo.UploadFileToServer)
m.Post("/upload-remove", bindIgnErr(auth.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer)
}, func(ctx *context.Context) {
if !setting.Repository.Upload.Enabled {
ctx.Handle(404, "", nil)
return
}
})
}, reqRepoWriter, context.RepoRef(), func(ctx *context.Context) {
if !ctx.Repo.Repository.CanEnableEditor() || ctx.Repo.IsViewCommit {
ctx.Handle(404, "", nil)
return
}
})
}, reqSignIn, context.RepoAssignment(), repo.MustBeNotBare, context.UnitTypes())
m.Group("/:username/:reponame", func() {
m.Group("", func() {
m.Get("/releases", repo.Releases)
m.Get("/^:type(issues|pulls)$", repo.RetrieveLabels, repo.Issues)
m.Get("/^:type(issues|pulls)$/:index", repo.ViewIssue)
m.Get("/labels/", repo.RetrieveLabels, repo.Labels)
m.Get("/milestones", repo.Milestones)
}, context.RepoRef())
// m.Get("/branches", repo.Branches)
m.Post("/branches/:name/delete", reqSignIn, reqRepoWriter, repo.DeleteBranchPost)
m.Group("/wiki", func() {
m.Get("/?:page", repo.Wiki)
m.Get("/_pages", repo.WikiPages)
m.Group("", func() {
m.Combo("/_new").Get(repo.NewWiki).
Post(bindIgnErr(auth.NewWikiForm{}), repo.NewWikiPost)
m.Combo("/:page/_edit").Get(repo.EditWiki).
Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost)
m.Post("/:page/delete", repo.DeleteWikiPagePost)
}, reqSignIn, reqRepoWriter)
}, repo.MustEnableWiki, context.RepoRef())
m.Group("/wiki", func() {
m.Get("/raw/*", repo.WikiRaw)
m.Get("/*", repo.WikiRaw)
}, repo.MustEnableWiki)
m.Get("/archive/*", repo.Download)
m.Group("/pulls/:index", func() {
m.Get("/commits", context.RepoRef(), repo.ViewPullCommits)
m.Get("/files", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ViewPullFiles)
m.Post("/merge", reqRepoWriter, repo.MergePullRequest)
}, repo.MustAllowPulls)
m.Group("", func() {
m.Get("/src/*", repo.SetEditorconfigIfExists, repo.Home)
m.Get("/raw/*", repo.SingleDownload)
m.Get("/commits/*", repo.RefCommits)
m.Get("/graph", repo.Graph)
m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff)
m.Get("/forks", repo.Forks)
}, context.RepoRef())
m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", repo.RawDiff)
m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.CompareDiff)
}, ignSignIn, context.RepoAssignment(), repo.MustBeNotBare, context.UnitTypes())
m.Group("/:username/:reponame", func() {
m.Get("/stars", repo.Stars)
m.Get("/watchers", repo.Watchers)
}, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes())
m.Group("/:username", func() {
m.Group("/:reponame", func() {
m.Get("", repo.SetEditorconfigIfExists, repo.Home)
m.Get("\\.git$", repo.SetEditorconfigIfExists, repo.Home)
}, ignSignIn, context.RepoAssignment(true), context.RepoRef(), context.UnitTypes())
m.Group("/:reponame", func() {
m.Group("/info/lfs", func() {
m.Post("/objects/batch", lfs.BatchHandler)
m.Get("/objects/:oid/:filename", lfs.ObjectOidHandler)
m.Any("/objects/:oid", lfs.ObjectOidHandler)
m.Post("/objects", lfs.PostHandler)
}, ignSignInAndCsrf)
m.Any("/*", ignSignInAndCsrf, repo.HTTP)
m.Head("/tasks/trigger", repo.TriggerTask)
})
})
// ***** END: Repository *****
m.Group("/notifications", func() {
m.Get("", user.Notifications)
m.Post("/status", user.NotificationStatusPost)
}, reqSignIn)
m.Group("/api", func() {
apiv1.RegisterRoutes(m)
}, ignSignIn)
// robots.txt
m.Get("/robots.txt", func(ctx *context.Context) {
if setting.HasRobotsTxt {
ctx.ServeFileContent(path.Join(setting.CustomPath, "robots.txt"))
} else {
ctx.Error(404)
}
})
// Not found handler.
m.NotFound(routers.NotFound)
// Flag for port number in case first time run conflict.
if ctx.IsSet("port") {
setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, ctx.String("port"), 1)
setting.HTTPPort = ctx.String("port")
switch setting.Protocol {
case setting.UnixSocket:
case setting.FCGI:
case setting.FCGIUnix:
default:
// Save LOCAL_ROOT_URL if port changed
cfg := ini.Empty()
if com.IsFile(setting.CustomConf) {
// Keeps custom settings if there is already something.
if err := cfg.Append(setting.CustomConf); err != nil {
return fmt.Errorf("Failed to load custom conf '%s': %v", setting.CustomConf, err)
}
}
defaultLocalURL := string(setting.Protocol) + "://"
if setting.HTTPAddr == "0.0.0.0" {
defaultLocalURL += "localhost"
} else {
defaultLocalURL += setting.HTTPAddr
}
defaultLocalURL += ":" + setting.HTTPPort + "/"
cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
if err := cfg.SaveTo(setting.CustomConf); err != nil {
return fmt.Errorf("Error saving generated JWT Secret to custom config: %v", err)
}
}
}
listenAddr := setting.HTTPAddr
if setting.Protocol != setting.UnixSocket && setting.Protocol != setting.FCGIUnix {
listenAddr += ":" + setting.HTTPPort
var listenAddr string
if setting.Protocol == setting.UnixSocket {
listenAddr = fmt.Sprintf("%s", setting.HTTPAddr)
} else {
listenAddr = fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.HTTPPort)
}
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
@@ -166,7 +666,6 @@ func runWeb(ctx *cli.Context) error {
if setting.EnablePprof {
go func() {
log.Info("Starting pprof server on localhost:6060")
log.Info("%v", http.ListenAndServe("localhost:6060", nil))
}()
}
@@ -174,38 +673,34 @@ func runWeb(ctx *cli.Context) error {
var err error
switch setting.Protocol {
case setting.HTTP:
NoHTTPRedirector()
err = runHTTP("tcp", listenAddr, context2.ClearHandler(m))
err = runHTTP(listenAddr, context2.ClearHandler(m))
case setting.HTTPS:
if setting.EnableLetsEncrypt {
err = runLetsEncrypt(listenAddr, setting.Domain, setting.LetsEncryptDirectory, setting.LetsEncryptEmail, context2.ClearHandler(m))
break
}
if setting.RedirectOtherPort {
go runHTTPRedirector()
} else {
NoHTTPRedirector()
}
err = runHTTPS("tcp", listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
err = runHTTPS(listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
case setting.FCGI:
NoHTTPRedirector()
err = runFCGI("tcp", listenAddr, context2.ClearHandler(m))
err = fcgi.Serve(nil, context2.ClearHandler(m))
case setting.UnixSocket:
NoHTTPRedirector()
err = runHTTP("unix", listenAddr, context2.ClearHandler(m))
case setting.FCGIUnix:
NoHTTPRedirector()
err = runFCGI("unix", listenAddr, context2.ClearHandler(m))
if err := os.Remove(listenAddr); err != nil && !os.IsNotExist(err) {
log.Fatal(4, "Failed to remove unix socket directory %s: %v", listenAddr, err)
}
var listener *net.UnixListener
listener, err = net.ListenUnix("unix", &net.UnixAddr{Name: listenAddr, Net: "unix"})
if err != nil {
break // Handle error after switch
}
// FIXME: add proper implementation of signal capture on all protocols
// execute this on SIGTERM or SIGINT: listener.Close()
if err = os.Chmod(listenAddr, os.FileMode(setting.UnixSocketPermission)); err != nil {
log.Fatal(4, "Failed to set permission of unix socket: %v", err)
}
err = http.Serve(listener, context2.ClearHandler(m))
default:
log.Fatal("Invalid protocol: %s", setting.Protocol)
log.Fatal(4, "Invalid protocol: %s", setting.Protocol)
}
if err != nil {
log.Critical("Failed to start server: %v", err)
log.Fatal(4, "Failed to start server: %v", err)
}
log.Info("HTTP Listener: %s Closed", listenAddr)
<-graceful.GetManager().Done()
log.Info("PID: %d Gitea Web Finished", os.Getpid())
log.Close()
return nil
}

View File

@@ -1,3 +1,5 @@
// +build !windows
// Copyright 2016 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.
@@ -6,47 +8,37 @@ package cmd
import (
"crypto/tls"
"net"
"net/http"
"net/http/fcgi"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"github.com/facebookgo/grace/gracehttp"
)
func runHTTP(network, listenAddr string, m http.Handler) error {
return graceful.HTTPListenAndServe(network, listenAddr, m)
}
func runHTTPS(network, listenAddr, certFile, keyFile string, m http.Handler) error {
return graceful.HTTPListenAndServeTLS(network, listenAddr, certFile, keyFile, m)
}
func runHTTPSWithTLSConfig(network, listenAddr string, tlsConfig *tls.Config, m http.Handler) error {
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, tlsConfig, m)
}
// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
func NoHTTPRedirector() {
graceful.GetManager().InformCleanup()
}
// NoMainListener tells our cleanup routine that we will not be using a possibly provided listener
// for our main HTTP/HTTPS service
func NoMainListener() {
graceful.GetManager().InformCleanup()
}
func runFCGI(network, listenAddr string, m http.Handler) error {
// This needs to handle stdin as fcgi point
fcgiServer := graceful.NewServer(network, listenAddr)
err := fcgiServer.ListenAndServe(func(listener net.Listener) error {
return fcgi.Serve(listener, m)
func runHTTP(listenAddr string, m http.Handler) error {
return gracehttp.Serve(&http.Server{
Addr: listenAddr,
Handler: m,
})
}
func runHTTPS(listenAddr, certFile, keyFile string, m http.Handler) error {
config := &tls.Config{
MinVersion: tls.VersionTLS10,
}
if config.NextProtos == nil {
config.NextProtos = []string{"http/1.1"}
}
config.Certificates = make([]tls.Certificate, 1)
var err error
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
log.Fatal(4, "Failed to load https cert file %s: %v", listenAddr, err)
}
return gracehttp.Serve(&http.Server{
Addr: listenAddr,
Handler: m,
TLSConfig: config,
})
if err != nil {
log.Fatal("Failed to start FCGI main server: %v", err)
}
log.Info("FCGI Listener: %s Closed", listenAddr)
return err
}

19
cmd/web_windows.go Normal file
View File

@@ -0,0 +1,19 @@
// +build windows
// Copyright 2016 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 cmd
import (
"net/http"
)
func runHTTP(listenAddr string, m http.Handler) error {
return http.ListenAndServe(listenAddr, m)
}
func runHTTPS(listenAddr, certFile, keyFile string, m http.Handler) error {
return http.ListenAndServeTLS(listenAddr, certFile, keyFile, m)
}

469
conf/app.ini vendored Normal file
View File

@@ -0,0 +1,469 @@
; App name that shows on every page title
APP_NAME = Gitea: Git with a cup of tea
; Change it if you run locally
RUN_USER = git
; Either "dev", "prod" or "test", default is "dev"
RUN_MODE = dev
[repository]
ROOT =
SCRIPT_TYPE = bash
; Default ANSI charset
ANSI_CHARSET =
; Force every new repository to be private
FORCE_PRIVATE = false
; Global maximum creation limit of repository per user, -1 means no limit
MAX_CREATION_LIMIT = -1
; Mirror sync queue length, increase if mirror syncing starts hanging
MIRROR_QUEUE_LENGTH = 1000
; Patch test queue length, increase if pull request patch testing starts hanging
PULL_REQUEST_QUEUE_LENGTH = 1000
; Preferred Licenses to place at the top of the List
; Name must match file name in conf/license or custom/conf/license
PREFERRED_LICENSES = Apache License 2.0,MIT License
; Disable ability to interact with repositories by HTTP protocol
DISABLE_HTTP_GIT = false
[repository.editor]
; List of file extensions that should have line wraps in the CodeMirror editor
; Separate extensions with a comma. To line wrap files w/o extension, just put a comma
LINE_WRAP_EXTENSIONS = .txt,.md,.markdown,.mdown,.mkd,
; Valid file modes that have a preview API associated with them, such as api/v1/markdown
; Separate values by commas. Preview tab in edit mode won't show if the file extension doesn't match
PREVIEWABLE_FILE_MODES = markdown
[repository.upload]
; Whether repository file uploads are enabled. Defaults to `true`
ENABLED = true
; Path for uploads. Defaults to `data/tmp/uploads` (tmp gets deleted on gitea restart)
TEMP_PATH = data/tmp/uploads
; One or more allowed types, e.g. image/jpeg|image/png. Nothing means any file type
ALLOWED_TYPES =
; Max size of each file in MB. Defaults to 3MB
FILE_MAX_SIZE = 3
; Max number of files per upload. Defaults to 5
MAX_FILES = 5
[ui]
; Number of repositories that are showed in one explore page
EXPLORE_PAGING_NUM = 20
; Number of issues that are showed in one page
ISSUE_PAGING_NUM = 10
; Number of maximum commits showed in one activity feed
FEED_MAX_COMMIT_NUM = 5
; Value of `theme-color` meta tag, used by Android >= 5.0
; An invalid color like "none" or "disable" will have the default style
; More info: https://developers.google.com/web/updates/2014/11/Support-for-theme-color-in-Chrome-39-for-Android
THEME_COLOR_META_TAG = `#6cc644`
; Max size of files to be displayed (defaults is 8MiB)
MAX_DISPLAY_FILE_SIZE = 8388608
; Whether show the user email in the Explore Users page
SHOW_USER_EMAIL = true
[ui.admin]
; Number of users that are showed in one page
USER_PAGING_NUM = 50
; Number of repos that are showed in one page
REPO_PAGING_NUM = 50
; Number of notices that are showed in one page
NOTICE_PAGING_NUM = 25
; Number of organization that are showed in one page
ORG_PAGING_NUM = 50
[ui.user]
; Number of repos that are showed in one page
REPO_PAGING_NUM = 15
[markdown]
; Enable hard line break extension
ENABLE_HARD_LINE_BREAK = false
; List of custom URL-Schemes that are allowed as links when rendering Markdown
; for example git,magnet
CUSTOM_URL_SCHEMES =
; List of file extensions that should be rendered/edited as Markdown
; Separate extensions with a comma. To render files w/o extension as markdown, just put a comma
FILE_EXTENSIONS = .md,.markdown,.mdown,.mkd
[server]
PROTOCOL = http
DOMAIN = localhost
ROOT_URL = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
HTTP_ADDR = 0.0.0.0
HTTP_PORT = 3000
; Permission for unix socket
UNIX_SOCKET_PERMISSION = 666
; Local (DMZ) URL for Gitea workers (such as SSH update) accessing web service.
; In 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.
LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
; Disable SSH feature when not available
DISABLE_SSH = false
; Whether use builtin SSH server or not.
START_SSH_SERVER = false
; Domain name to be exposed in clone URL
SSH_DOMAIN = %(DOMAIN)s
; Network interface builtin SSH server listens on
SSH_LISTEN_HOST =
; Port number to be exposed in clone URL
SSH_PORT = 22
; Port number builtin SSH server listens on
SSH_LISTEN_PORT = %(SSH_PORT)s
; Root path of SSH directory, default is '~/.ssh', but you have to use '/home/git/.ssh'.
SSH_ROOT_PATH =
; Directory to create temporary files when test publick key using ssh-keygen,
; default is system temporary directory.
SSH_KEY_TEST_PATH =
; Path to ssh-keygen, default is 'ssh-keygen' and let shell find out which one to call.
SSH_KEYGEN_PATH = ssh-keygen
; Indicate whether to check minimum key size with corresponding type
MINIMUM_KEY_SIZE_CHECK = false
; Disable CDN even in "prod" mode
OFFLINE_MODE = false
DISABLE_ROUTER_LOG = false
; Generate steps:
; $ ./gitea cert -ca=true -duration=8760h0m0s -host=myhost.example.com
;
; Or from a .pfx file exported from the Windows certificate store (do
; not forget to export the private key):
; $ openssl pkcs12 -in cert.pfx -out cert.pem -nokeys
; $ openssl pkcs12 -in cert.pfx -out key.pem -nocerts -nodes
CERT_FILE = custom/https/cert.pem
KEY_FILE = custom/https/key.pem
; Upper level of template and static file path
; default is the path where Gitea is executed
STATIC_ROOT_PATH =
; Default path for App data
APP_DATA_PATH = data
; Application level GZIP support
ENABLE_GZIP = false
; Landing page for non-logged users, can be "home" or "explore"
LANDING_PAGE = home
; Define allowed algorithms and their minimum key length (use -1 to disable a type)
[ssh.minimum_key_sizes]
ED25519 = 256
ECDSA = 256
RSA = 2048
DSA = 1024
[database]
; Either "mysql", "postgres" or "sqlite3", it's your choice
DB_TYPE = mysql
HOST = 127.0.0.1:3306
NAME = gitea
USER = root
PASSWD =
; For "postgres" only, either "disable", "require" or "verify-full"
SSL_MODE = disable
; For "sqlite3" and "tidb", use absolute path when you start as service
PATH = data/gitea.db
[indexer]
ISSUE_INDEXER_PATH = indexers/issues.bleve
UPDATE_BUFFER_LEN = 20
[admin]
; Disable regular (non-admin) users to create organizations
DISABLE_REGULAR_ORG_CREATION = false
[security]
; Whether the installer is disabled
INSTALL_LOCK = false
; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!!
SECRET_KEY = !#@FDEWREWR&*(
; Auto-login remember days
LOGIN_REMEMBER_DAYS = 7
COOKIE_USERNAME = gitea_awesome
COOKIE_REMEMBER_NAME = gitea_incredible
; Reverse proxy authentication header name of user name
REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
; Sets the minimum password length for new Users
MIN_PASSWORD_LENGTH = 6
; True when users are allowed to import local server paths
IMPORT_LOCAL_PATHS = false
[service]
ACTIVE_CODE_LIVE_MINUTES = 180
RESET_PASSWD_CODE_LIVE_MINUTES = 180
; User need to confirm e-mail for registration
REGISTER_EMAIL_CONFIRM = false
; Does not allow register and admin create account only
DISABLE_REGISTRATION = false
; User must sign in to view anything.
REQUIRE_SIGNIN_VIEW = false
; Mail notification
ENABLE_NOTIFY_MAIL = false
; More detail: https://github.com/go-gitea/gitea/issues/165
ENABLE_REVERSE_PROXY_AUTHENTICATION = false
ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false
; Enable captcha validation for registration
ENABLE_CAPTCHA = true
; Default value for KeepEmailPrivate
; New user will get the value of this setting copied into their profile
DEFAULT_KEEP_EMAIL_PRIVATE = false
; Default value for the domain part of the user's email address in the git log
; if he has set KeepEmailPrivate true. The user's email replaced with a
; concatenation of the user name in lower case, "@" and NO_REPLY_ADDRESS.
NO_REPLY_ADDRESS = noreply.example.org
[webhook]
; Hook task queue length, increase if webhook shooting starts hanging
QUEUE_LENGTH = 1000
; Deliver timeout in seconds
DELIVER_TIMEOUT = 5
; Allow insecure certification
SKIP_TLS_VERIFY = false
; Number of history information in each page
PAGING_NUM = 10
[mailer]
ENABLED = false
; Buffer length of channel, keep it as it is if you don't know what it is.
SEND_BUFFER_LEN = 100
; Name displayed in mail title
SUBJECT = %(APP_NAME)s
; Mail server
; Gmail: smtp.gmail.com:587
; QQ: smtp.qq.com:465
; Note, if the port ends with "465", SMTPS will be used. Using STARTTLS on port 587 is recommended per RFC 6409. If the server supports STARTTLS it will always be used.
HOST =
; Disable HELO operation when hostname are different.
DISABLE_HELO =
; Custom hostname for HELO operation, default is from system.
HELO_HOSTNAME =
; Do not verify the certificate of the server. Only use this for self-signed certificates
SKIP_VERIFY =
; Use client certificate
USE_CERTIFICATE = false
CERT_FILE = custom/mailer/cert.pem
KEY_FILE = custom/mailer/key.pem
; Mail from address, RFC 5322. This can be just an email address, or the `"Name" <email@example.com>` format
FROM =
; Mailer user name and password
USER =
PASSWD =
; Use text/html as alternative format of content
ENABLE_HTML_ALTERNATIVE = false
; Enable sendmail (override SMTP)
USE_SENDMAIL = false
; Specifiy an alternative sendmail binary
SENDMAIL_PATH = sendmail
[cache]
; Either "memory", "redis", or "memcache", default is "memory"
ADAPTER = memory
; For "memory" only, GC interval in seconds, default is 60
INTERVAL = 60
; For "redis" and "memcache", connection host address
; redis: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180
; memcache: `127.0.0.1:11211`
HOST =
[session]
; Either "memory", "file", or "redis", default is "memory"
PROVIDER = memory
; Provider config options
; memory: not have any config yet
; file: session file path, e.g. `data/sessions`
; redis: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180
; mysql: go-sql-driver/mysql dsn config string, e.g. `root:password@/session_table`
PROVIDER_CONFIG = data/sessions
; Session cookie name
COOKIE_NAME = i_like_gitea
; If you use session in https only, default is false
COOKIE_SECURE = false
; Enable set cookie, default is true
ENABLE_SET_COOKIE = true
; Session GC time interval, default is 86400
GC_INTERVAL_TIME = 86400
; Session life time, default is 86400
SESSION_LIFE_TIME = 86400
[picture]
AVATAR_UPLOAD_PATH = data/avatars
; Chinese users can choose "duoshuo"
; or a custom avatar source, like: http://cn.gravatar.com/avatar/
GRAVATAR_SOURCE = gravatar
; This value will be forced to be true in offline mode.
DISABLE_GRAVATAR = false
; Federated avatar lookup uses DNS to discover avatar associated
; with emails, see https://www.libravatar.org
; This value will be forced to be false in offline mode or Gravatar is disbaled.
ENABLE_FEDERATED_AVATAR = false
[attachment]
; Whether attachments are enabled. Defaults to `true`
ENABLE = true
; Path for attachments. Defaults to `data/attachments`
PATH = data/attachments
; One or more allowed types, e.g. image/jpeg|image/png
ALLOWED_TYPES = image/jpeg|image/png|application/zip|application/gzip
; Max size of each file. Defaults to 32MB
MAX_SIZE = 4
; Max number of files per upload. Defaults to 10
MAX_FILES = 5
[time]
; Specifies the format for fully outputed dates. Defaults to RFC1123
; Special supported values are ANSIC, UnixDate, RubyDate, RFC822, RFC822Z, RFC850, RFC1123, RFC1123Z, RFC3339, RFC3339Nano, Kitchen, Stamp, StampMilli, StampMicro and StampNano
; For more information about the format see http://golang.org/pkg/time/#pkg-constants
FORMAT =
[log]
ROOT_PATH =
; Either "console", "file", "conn", "smtp" or "database", default is "console"
; Use comma to separate multiple modes, e.g. "console, file"
MODE = console
; Buffer length of channel, keep it as it is if you don't know what it is.
BUFFER_LEN = 10000
; Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace"
LEVEL = Trace
; For "console" mode only
[log.console]
LEVEL =
; For "file" mode only
[log.file]
LEVEL =
; This enables automated log rotate(switch of following options), default is true
LOG_ROTATE = true
; Max line number of single file, default is 1000000
MAX_LINES = 1000000
; Max size shift of single file, default is 28 means 1 << 28, 256MB
MAX_SIZE_SHIFT = 28
; Segment log daily, default is true
DAILY_ROTATE = true
; Expired days of log file(delete after max days), default is 7
MAX_DAYS = 7
; For "conn" mode only
[log.conn]
LEVEL =
; Reconnect host for every single message, default is false
RECONNECT_ON_MSG = false
; Try to reconnect when connection is lost, default is false
RECONNECT = false
; Either "tcp", "unix" or "udp", default is "tcp"
PROTOCOL = tcp
; Host address
ADDR =
; For "smtp" mode only
[log.smtp]
LEVEL =
; Name displayed in mail title, default is "Diagnostic message from server"
SUBJECT = Diagnostic message from server
; Mail server
HOST =
; Mailer user name and password
USER =
PASSWD =
; Receivers, can be one or more, e.g. 1@example.com,2@example.com
RECEIVERS =
; For "database" mode only
[log.database]
LEVEL =
; Either "mysql" or "postgres"
DRIVER =
; Based on xorm, e.g.: root:root@localhost/gitea?charset=utf8
CONN =
[cron]
; Enable running cron tasks periodically.
ENABLED = true
; Run cron tasks when Gitea starts.
RUN_AT_START = false
; Update mirrors
[cron.update_mirrors]
SCHEDULE = @every 10m
; Repository health check
[cron.repo_health_check]
SCHEDULE = @every 24h
TIMEOUT = 60s
; Arguments for command 'git fsck', e.g. "--unreachable --tags"
; see more on http://git-scm.com/docs/git-fsck/1.7.5
ARGS =
; Check repository statistics
[cron.check_repo_stats]
RUN_AT_START = true
SCHEDULE = @every 24h
; Clean up old repository archives
[cron.archive_cleanup]
RUN_AT_START = true
SCHEDULE = @every 24h
; Archives created more than OLDER_THAN ago are subject to deletion
OLDER_THAN = 24h
[git]
; Disables highlight of added and removed changes
DISABLE_DIFF_HIGHLIGHT = false
; Max number of lines allowed of a single file in diff view
MAX_GIT_DIFF_LINES = 1000
; Max number of characters of a line allowed in diff view
MAX_GIT_DIFF_LINE_CHARACTERS = 500
; Max number of files shown in diff view
MAX_GIT_DIFF_FILES = 100
; Arguments for command 'git gc', e.g. "--aggressive --auto"
; see more on http://git-scm.com/docs/git-gc/1.7.5
GC_ARGS =
; Operation timeout in seconds
[git.timeout]
MIGRATE = 600
MIRROR = 300
CLONE = 300
PULL = 300
GC = 60
[mirror]
; Default interval in hours between each check
DEFAULT_INTERVAL = 8
[api]
; Max number of items will response in a page
MAX_RESPONSE_ITEMS = 50
[i18n]
LANGS = en-US,zh-CN,zh-HK,zh-TW,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT,fi-FI,tr-TR,cs-CZ,sr-SP,sv-SE,ko-KR
NAMES = English,简体中文,繁體中文(香港),繁體中文(台灣),Deutsch,Français,Nederlands,Latviešu,Русский,日本語,Español,Português do Brasil,Polski,български,Italiano,Suomalainen,Türkçe,čeština,Српски,Svenska,한국어
; Used for datetimepicker
[i18n.datelang]
en-US = en
zh-CN = zh
zh-HK = zh-TW
zh-TW = zh-TW
de-DE = de
fr-FR = fr
nl-NL = nl
lv-LV = lv
ru-RU = ru
ja-JP = ja
es-ES = es
pt-BR = pt-BR
pl-PL = pl
bg-BG = bg
it-IT = it
fi-FI = fi
tr-TR = tr
cs-CZ = cs-CZ
sr-SP = sr
sv-SE = sv
ko-KR = ko
; Extension mapping to highlight class
; e.g. .toml=ini
[highlight.mapping]
[other]
SHOW_FOOTER_BRANDING = false
; Show version information about Gitea and Go in the footer
SHOW_FOOTER_VERSION = true
; Show time of template execution in the footer
SHOW_FOOTER_TEMPLATE_LOAD_TIME = true

View File

@@ -1,66 +0,0 @@
Environment To Ini
==================
Multiple docker users have requested that the Gitea docker is changed
to permit arbitrary configuration via environment variables.
Gitea needs to use an ini file for configuration because the running
environment that starts the docker may not be the same as that used
by the hooks. An ini file also gives a good default and means that
users do not have to completely provide a full environment.
With those caveats above, this command provides a generic way of
converting suitably structured environment variables into any ini
value.
To use the command is very simple just run it and the default gitea
app.ini will be rewritten to take account of the variables provided,
however there are various options to give slightly different
behavior and these can be interrogated with the `-h` option.
The environment variables should be of the form:
GITEA__SECTION_NAME__KEY_NAME
Environment variables are usually restricted to a reduced character
set "0-9A-Z_" - in order to allow the setting of sections with
characters outside of that set, they should be escaped as following:
"_0X2E_" for ".". The entire section and key names can be escaped as
a UTF8 byte string if necessary. E.g. to configure:
"""
...
[log.console]
COLORIZE=false
STDERR=true
...
"""
You would set the environment variables: "GITEA__LOG_0x2E_CONSOLE__COLORIZE=false"
and "GITEA__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found
on the configuration cheat sheet.
To plug this command in to the docker, you simply compile the provided go file using:
go build environment-to-ini.go
And copy the resulting `environment-to-ini` command to /app/gitea in the docker.
Apply the below patch to /etc/s6/gitea.setup to wire this in.
If you find this useful please comment on #7287
diff --git a/docker/root/etc/s6/gitea/setup b/docker/root/etc/s6/gitea/setup
index f87ce9115..565bfcba9 100755
--- a/docker/root/etc/s6/gitea/setup
+++ b/docker/root/etc/s6/gitea/setup
@@ -44,6 +44,8 @@ if [ ! -f ${GITEA_CUSTOM}/conf/app.ini ]; then
SECRET_KEY=${SECRET_KEY:-""} \
envsubst < /etc/templates/app.ini > ${GITEA_CUSTOM}/conf/app.ini
+ /app/gitea/environment-to-ini -c ${GITEA_CUSTOM}/conf/app.ini
+
chown ${USER}:git ${GITEA_CUSTOM}/conf/app.ini
fi

View File

@@ -1,224 +0,0 @@
// Copyright 2019 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 main
import (
"os"
"regexp"
"strconv"
"strings"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/unknwon/com"
"github.com/urfave/cli"
ini "gopkg.in/ini.v1"
)
// EnvironmentPrefix environment variables prefixed with this represent ini values to write
const EnvironmentPrefix = "GITEA"
func main() {
app := cli.NewApp()
app.Name = "environment-to-ini"
app.Usage = "Use provided environment to update configuration ini"
app.Description = `As a helper to allow docker users to update the gitea configuration
through the environment, this command allows environment variables to
be mapped to values in the ini.
Environment variables of the form "GITEA__SECTION_NAME__KEY_NAME"
will be mapped to the ini section "[section_name]" and the key
"KEY_NAME" with the value as provided.
Environment variables are usually restricted to a reduced character
set "0-9A-Z_" - in order to allow the setting of sections with
characters outside of that set, they should be escaped as following:
"_0X2E_" for ".". The entire section and key names can be escaped as
a UTF8 byte string if necessary. E.g. to configure:
"""
...
[log.console]
COLORIZE=false
STDERR=true
...
"""
You would set the environment variables: "GITEA__LOG_0x2E_CONSOLE__COLORIZE=false"
and "GITEA__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found
on the configuration cheat sheet.`
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "custom-path, C",
Value: setting.CustomPath,
Usage: "Custom path file path",
},
cli.StringFlag{
Name: "config, c",
Value: setting.CustomConf,
Usage: "Custom configuration file path",
},
cli.StringFlag{
Name: "work-path, w",
Value: setting.AppWorkPath,
Usage: "Set the gitea working path",
},
cli.StringFlag{
Name: "out, o",
Value: "",
Usage: "Destination file to write to",
},
cli.BoolFlag{
Name: "clear",
Usage: "Clears the matched variables from the environment",
},
cli.StringFlag{
Name: "prefix, p",
Value: EnvironmentPrefix,
Usage: "Environment prefix to look for - will be suffixed by __ (2 underscores)",
},
}
app.Action = runEnvironmentToIni
setting.SetCustomPathAndConf("", "", "")
err := app.Run(os.Args)
if err != nil {
log.Fatal("Failed to run app with %s: %v", os.Args, err)
}
}
func runEnvironmentToIni(c *cli.Context) error {
providedCustom := c.String("custom-path")
providedConf := c.String("config")
providedWorkPath := c.String("work-path")
setting.SetCustomPathAndConf(providedCustom, providedConf, providedWorkPath)
cfg := ini.Empty()
if com.IsFile(setting.CustomConf) {
if err := cfg.Append(setting.CustomConf); err != nil {
log.Fatal("Failed to load custom conf '%s': %v", setting.CustomConf, err)
}
} else {
log.Warn("Custom config '%s' not found, ignore this if you're running first time", setting.CustomConf)
}
cfg.NameMapper = ini.SnackCase
prefix := c.String("prefix") + "__"
for _, kv := range os.Environ() {
idx := strings.IndexByte(kv, '=')
if idx < 0 {
continue
}
eKey := kv[:idx]
value := kv[idx+1:]
if !strings.HasPrefix(eKey, prefix) {
continue
}
eKey = eKey[len(prefix):]
sectionName, keyName := DecodeSectionKey(eKey)
if len(keyName) == 0 {
continue
}
section, err := cfg.GetSection(sectionName)
if err != nil {
section, err = cfg.NewSection(sectionName)
if err != nil {
log.Error("Error creating section: %s : %v", sectionName, err)
continue
}
}
key := section.Key(keyName)
if key == nil {
key, err = section.NewKey(keyName, value)
if err != nil {
log.Error("Error creating key: %s in section: %s with value: %s : %v", keyName, sectionName, value, err)
continue
}
}
key.SetValue(value)
}
destination := c.String("out")
if len(destination) == 0 {
destination = setting.CustomConf
}
err := cfg.SaveTo(destination)
if err != nil {
return err
}
if c.Bool("clear") {
for _, kv := range os.Environ() {
idx := strings.IndexByte(kv, '=')
if idx < 0 {
continue
}
eKey := kv[:idx]
if strings.HasPrefix(eKey, prefix) {
_ = os.Unsetenv(eKey)
}
}
}
return nil
}
const escapeRegexpString = "_0[xX](([0-9a-fA-F][0-9a-fA-F])+)_"
var escapeRegex = regexp.MustCompile(escapeRegexpString)
// DecodeSectionKey will decode a portable string encoded Section__Key pair
// Portable strings are considered to be of the form [A-Z0-9_]*
// We will encode a disallowed value as the UTF8 byte string preceded by _0X and
// followed by _. E.g. _0X2C_ for a '-' and _0X2E_ for '.'
// Section and Key are separated by a plain '__'.
// The entire section can be encoded as a UTF8 byte string
func DecodeSectionKey(encoded string) (string, string) {
section := ""
key := ""
inKey := false
last := 0
escapeStringIndices := escapeRegex.FindAllStringIndex(encoded, -1)
for _, unescapeIdx := range escapeStringIndices {
preceding := encoded[last:unescapeIdx[0]]
if !inKey {
if splitter := strings.Index(preceding, "__"); splitter > -1 {
section += preceding[:splitter]
inKey = true
key += preceding[splitter+2:]
} else {
section += preceding
}
} else {
key += preceding
}
toDecode := encoded[unescapeIdx[0]+3 : unescapeIdx[1]-1]
decodedBytes := make([]byte, len(toDecode)/2)
for i := 0; i < len(toDecode)/2; i++ {
// Can ignore error here as we know these should be hexadecimal from the regexp
byteInt, _ := strconv.ParseInt(toDecode[2*i:2*i+2], 16, 0)
decodedBytes[i] = byte(byteInt)
}
if inKey {
key += string(decodedBytes)
} else {
section += string(decodedBytes)
}
last = unescapeIdx[1]
}
remaining := encoded[last:]
if !inKey {
if splitter := strings.Index(remaining, "__"); splitter > -1 {
section += remaining[:splitter]
inKey = true
key += remaining[splitter+2:]
} else {
section += remaining
}
} else {
key += remaining
}
return section, key
}

View File

@@ -1,42 +0,0 @@
#!/bin/bash
########################################################################
# This script some defaults for gitea to run in a FHS compliant manner #
########################################################################
# It assumes that you place this script as gitea in /usr/bin
#
# And place the original in /usr/lib/gitea with working files in /var/lib/gitea
# and main configuration in /etc/gitea/app.ini
GITEA="/usr/lib/gitea/gitea"
WORK_DIR="/var/lib/gitea"
APP_INI="/etc/gitea/app.ini"
APP_INI_SET=""
for i in "$@"; do
case "$i" in
"-c")
APP_INI_SET=1
;;
"-c="*)
APP_INI_SET=1
;;
"--config")
APP_INI_SET=1
;;
"--config="*)
APP_INI_SET=1
;;
*)
;;
esac
done
if [ -z "$APP_INI_SET" ]; then
CONF_ARG="-c \"$APP_INI\""
fi
# Provide FHS compliant defaults to
GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" "$GITEA" $CONF_ARG "$@"

View File

@@ -1,12 +0,0 @@
# IDE and code editor configuration
## Table of Contents
- [IDE and code editor configuration](#ide-and-code-editor-configuration)
- [Microsoft Visual Studio Code](#microsoft-visual-studio-code)
## Microsoft Visual Studio Code
Download Microsoft Visual Studio Code at https://code.visualstudio.com/ and follow instructions at https://code.visualstudio.com/docs/languages/go to setup Go extension for it.
Create new directory `.vscode` in Gitea root folder and copy contents of folder [contrib/ide/vscode](vscode/) to it. You can now use `Ctrl`+`Shift`+`B` to build gitea executable and `F5` to run it in debug mode.
Supported on Debian, Ubuntu, Red Hat, Fedora, SUSE Linux, MacOS and Microsoft Windows.

View File

@@ -1,31 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch",
"type": "go",
"request": "launch",
"mode": "debug",
"buildFlags": "",
"port": 2345,
"host": "127.0.0.1",
"program": "${workspaceRoot}/main.go",
"env": {},
"args": ["web"],
"showLog": true
},
{
"name": "Launch (with SQLite3)",
"type": "go",
"request": "launch",
"mode": "debug",
"buildFlags": "-tags=\"sqlite sqlite_unlock_notify\"",
"port": 2345,
"host": "127.0.0.1",
"program": "${workspaceRoot}/main.go",
"env": {},
"args": ["web"],
"showLog": true
}
]
}

View File

@@ -1,51 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"taskName": "Build",
"type": "shell",
"command": "go",
"group": "build",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared"
},
"args": ["build"],
"linux": {
"args": [ "-o", "gitea", "${workspaceRoot}/main.go" ]
},
"osx": {
"args": [ "-o", "gitea", "${workspaceRoot}/main.go" ]
},
"windows": {
"args": [ "-o", "gitea.exe", "\"${workspaceRoot}\\main.go\""]
},
"problemMatcher": ["$go"]
},
{
"taskName": "Build (with SQLite3)",
"type": "shell",
"command": "go",
"group": "build",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared"
},
"args": ["build", "-tags=\"sqlite sqlite_unlock_notify\""],
"linux": {
"args": ["-o", "gitea", "${workspaceRoot}/main.go"]
},
"osx": {
"args": ["-o", "gitea", "${workspaceRoot}/main.go"]
},
"windows": {
"args": ["-o", "gitea.exe", "\"${workspaceRoot}\\main.go\""]
},
"problemMatcher": ["$go"]
}
]
}

View File

@@ -1,93 +0,0 @@
#!/bin/sh
#
# /etc/rc.d/init.d/gitea
#
# Runs the Gitea Git with a cup of tea.
#
#
# chkconfig: - 85 15
#
### BEGIN INIT INFO
# Provides: gitea
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start gitea at boot time.
# Description: Control gitea.
### END INIT INFO
# Source function library.
. /etc/init.d/functions
# Default values
NAME=gitea
GITEA_HOME=/var/lib/${NAME}
GITEA_PATH=/usr/local/bin/${NAME}
GITEA_USER=git
SERVICENAME="Gitea - Git with a cup of tea"
LOCKFILE=/var/lock/subsys/gitea
LOGPATH=${GITEA_HOME}/log
LOGFILE=${LOGPATH}/gitea.log
RETVAL=0
# Read configuration from /etc/sysconfig/gitea to override defaults
[ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
# Don't do anything if nothing is installed
[ -x ${GITEA_PATH} ] || exit 0
# exit if logpath dir is not created.
[ -x ${LOGPATH} ] || exit 0
DAEMON_OPTS="--check $NAME"
# Set additional options, if any
[ ! -z "$GITEA_USER" ] && DAEMON_OPTS="$DAEMON_OPTS --user=${GITEA_USER}"
start() {
cd ${GITEA_HOME}
echo -n "Starting ${SERVICENAME}: "
daemon $DAEMON_OPTS "${GITEA_PATH} web -c /etc/${NAME}/app.ini > ${LOGFILE} 2>&1 &"
RETVAL=$?
echo
[ $RETVAL = 0 ] && touch ${LOCKFILE}
return $RETVAL
}
stop() {
cd ${GITEA_HOME}
echo -n "Shutting down ${SERVICENAME}: "
killproc ${NAME}
RETVAL=$?
echo
[ $RETVAL = 0 ] && rm -f ${LOCKFILE}
}
case "$1" in
start)
status ${NAME} > /dev/null 2>&1 && exit 0
start
;;
stop)
stop
;;
status)
status ${NAME}
;;
restart)
stop
start
;;
reload)
stop
start
;;
*)
echo "Usage: ${NAME} {start|stop|status|restart}"
exit 1
;;
esac
exit $RETVAL

View File

@@ -1,89 +0,0 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: gitea
# Required-Start: $syslog $network
# Required-Stop: $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: A self-hosted Git service written in Go.
# Description: A self-hosted Git service written in Go.
### END INIT INFO
# Author: Danny Boisvert
# Do NOT "set -e"
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
DESC="Gitea - Git with a cup of tea"
NAME=gitea
SERVICEVERBOSE=yes
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
WORKINGDIR=/var/lib/$NAME
DAEMON=/usr/local/bin/$NAME
DAEMON_ARGS="web -c /etc/$NAME/app.ini"
USER=git
USERBIND=""
# If you want to bind Gitea to a port below 1024 uncomment
# the line below
#USERBIND="setcap cap_net_bind_service=+ep"
STOP_SCHEDULE="${STOP_SCHEDULE:-QUIT/5/TERM/1/KILL/5}"
# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0
do_start()
{
$USERBIND $DAEMON
sh -c "USER=$USER HOME=/home/$USER GITEA_WORK_DIR=$WORKINGDIR start-stop-daemon --start --quiet --pidfile $PIDFILE --make-pidfile \\
--background --chdir $WORKINGDIR --chuid $USER \\
--exec $DAEMON -- $DAEMON_ARGS"
}
do_stop()
{
start-stop-daemon --stop --quiet --retry=$STOP_SCHEDULE --pidfile $PIDFILE --name $NAME --oknodo
rm -f $PIDFILE
}
do_status()
{
if [ -f $PIDFILE ]; then
if kill -0 $(cat "$PIDFILE"); then
echo "$NAME is running, PID is $(cat $PIDFILE)"
else
echo "$NAME process is dead, but pidfile exists"
fi
else
echo "$NAME is not running"
fi
}
case "$1" in
start)
echo "Starting $DESC" "$NAME"
do_start
;;
stop)
echo "Stopping $DESC" "$NAME"
do_stop
;;
status)
do_status
;;
restart)
echo "Restarting $DESC" "$NAME"
do_stop
do_start
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|status|restart}" >&2
exit 2
;;
esac
exit 0

View File

@@ -1,48 +0,0 @@
#!/bin/sh
#
# $FreeBSD$
#
# PROVIDE: gitea
# REQUIRE: NETWORKING SYSLOG
# KEYWORD: shutdown
#
# Add the following lines to /etc/rc.conf to enable gitea:
#
#gitea_enable="YES"
. /etc/rc.subr
name="gitea"
rcvar="gitea_enable"
load_rc_config $name
: ${gitea_user:="git"}
: ${gitea_enable:="NO"}
: ${gitea_directory:="/var/lib/gitea"}
command="/usr/local/bin/gitea web -c /etc/gitea/app.ini"
procname="$(echo $command |cut -d' ' -f1)"
pidfile="${gitea_directory}/${name}.pid"
start_cmd="${name}_start"
stop_cmd="${name}_stop"
gitea_start() {
cd ${gitea_directory}
export USER=${gitea_user}
export HOME=/usr/home/${gitea_user}
export GITEA_WORK_DIR=${gitea_directory}
/usr/sbin/daemon -f -u ${gitea_user} -p ${pidfile} $command
}
gitea_stop() {
if [ ! -f $pidfile ]; then
echo "GITEA PID File not found. Maybe GITEA is not running?"
else
kill $(cat $pidfile)
fi
}
run_rc_command "$1"

View File

@@ -1,15 +0,0 @@
#!/sbin/openrc-run
DIR=/var/lib/gitea
USER=git
start_stop_daemon_args="--user ${USER} --chdir ${DIR}"
command="/usr/local/bin/gitea"
command_args="web -c /etc/gitea/app.ini"
command_background=yes
pidfile=/var/run/gitea.pid
depend()
{
need net
}

View File

@@ -1,19 +0,0 @@
#!/bin/sh
#
# $OpenBSD$
daemon="/usr/local/bin/gitea"
daemon_user="git"
daemon_flags="web -c /etc/gitea/app.ini"
gitea_directory="/var/lib/gitea"
rc_bg=YES
. /etc/rc.d/rc.subr
rc_start() {
${rcexec} "cd ${gitea_directory}; ${daemon} ${daemon_flags} ${_bg}"
}
rc_cmd $1

View File

@@ -1,46 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<service_bundle type="manifest" name="export">
<service name="gitea" type="service" version="1">
<create_default_instance enabled="false"/>
<dependency name="network" grouping="require_all" restart_on="refresh" type="service">
<service_fmri value="svc:/milestone/network:default"/>
</dependency>
<dependency name="filesystem" grouping="require_all" restart_on="refresh" type="service">
<service_fmri value="svc:/system/filesystem/local"/>
</dependency>
<exec_method
type="method"
name="start"
exec="/opt/local/bin/gitea web"
timeout_seconds="60">
<method_context>
<method_credential user="git" group="git" />
<method_environment>
<envvar name='GITEA_WORK_DIR' value='/opt/local/share/gitea'/>
<envvar name='GITEA_CUSTOM' value='/opt/local/etc/gitea'/>
<envvar name='HOME' value='/var/db/gitea'/>
<envvar name='PATH' value='/opt/local/bin:${PATH}'/>
<envvar name='USER' value='git'/>
</method_environment>
</method_context>
</exec_method>
<exec_method type="method" name="stop" exec=":kill" timeout_seconds="60"/>
<property_group name="application" type="application"></property_group>
<property_group name="startd" type="framework">
<propval name="duration" type="astring" value="child"/>
<propval name="ignore_error" type="astring" value="core,signal"/>
</property_group>
<template>
<common_name>
<loctext xml:lang="C">A painless, self-hosted Git service</loctext>
</common_name>
</template>
</service>
</service_bundle>

View File

@@ -1,115 +0,0 @@
#!/bin/sh
#
# /etc/init.d/gitea
#
# Runs the Gitea Git with a cup of tea.
#
### BEGIN INIT INFO
# Provides: gitea
# Required-Start: $remote_fs
# Required-Stop: $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start gitea at boot time.
# Description: Control gitea.
### END INIT INFO
# Default values
NAME=gitea
GITEA_HOME=/var/lib/$NAME
GITEA_PATH=/usr/local/bin/$NAME
GITEA_USER=git
SERVICENAME="Gitea - Git with a cup of tea"
LOCKFILE=/var/lock/subsys/gitea
LOGPATH=${GITEA_HOME}/log
LOGFILE=${LOGPATH}/error.log
# gitea creates its own gitea.log from stdout
RETVAL=0
# Read configuration from /etc/sysconfig/gitea to override defaults
[ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
# Don't do anything if nothing is installed
test -x ${GITEA_PATH} || { echo "$NAME not installed";
if [ "$1" = "stop" ]; then exit 0;
else exit 5; fi; }
# exit if logpath dir is not created.
test -r ${LOGPATH} || { echo "$LOGPATH not existing";
if [ "$1" = "stop" ]; then exit 0;
else exit 6; fi; }
# Source function library.
. /etc/rc.status
# Reset status of this service
rc_reset
case "$1" in
start)
echo -n "Starting ${SERVICENAME} "
# As we can't use startproc, we have to check ourselves if the service is already running
/sbin/checkproc ${GITEA_PATH}
if [ $? -eq 0 ]; then
# return skipped as service is already running
(exit 5)
else
su - ${GITEA_USER} -c "USER=${GITEA_USER} GITEA_WORK_DIR=${GITEA_HOME} ${GITEA_PATH} web -c /etc/${NAME}/app.ini 2>&1 >>${LOGFILE} &"
fi
# Remember status and be verbose
rc_status -v
;;
stop)
echo -n "Shutting down ${SERVICENAME} "
## Stop daemon with killproc(8) and if this fails
## killproc sets the return value according to LSB.
/sbin/killproc ${GITEA_PATH}
# Remember status and be verbose
rc_status -v
;;
restart)
## Stop the service and regardless of whether it was
## running or not, start it again.
$0 stop
$0 start
# Remember status and be quiet
rc_status
;;
status)
echo -n "Checking for ${SERVICENAME} "
## Check status with checkproc(8), if process is running
## checkproc will return with exit status 0.
# Return value is slightly different for the status command:
# 0 - service up and running
# 1 - service dead, but /var/run/ pid file exists
# 2 - service dead, but /var/lock/ lock file exists
# 3 - service not running (unused)
# 4 - service status unknown :-(
# 5--199 reserved (5--99 LSB, 100--149 distro, 150--199 appl.)
# NOTE: checkproc returns LSB compliant status values.
/sbin/checkproc ${GITEA_PATH}
# NOTE: rc_status knows that we called this init script with
# "status" option and adapts its messages accordingly.
rc_status -v
;;
*)
echo "Usage: $0 {start|stop|status|restart}"
exit 1
;;
esac
rc_exit

View File

@@ -1,107 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: gitea
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitea
namespace: gitea
labels:
app: gitea
spec:
replicas: 1
template:
metadata:
name: gitea
labels:
app: gitea
spec:
containers:
- name: gitea
image: gitea/gitea:latest
imagePullPolicy: Always
volumeMounts:
- mountPath: "/var/lib/gitea"
name: "root"
- mountPath: "/data"
name: "data"
ports:
- containerPort: 22
name: ssh
protocol: TCP
- containerPort: 3000
name: http
protocol: TCP
restartPolicy: Always
volumes:
# Set up a data directory for gitea
# For production usage, you should consider using PV/PVC instead(or simply using storage like NAS)
# For more details, please see https://kubernetes.io/docs/concepts/storage/volumes/
- name: "root"
hostPath:
# directory location on host
path: "/var/lib/gitea"
# this field is optional
type: Directory
- name: "data"
hostPath:
path: "/data/gitea"
type: Directory
selector:
matchLabels:
app: gitea
---
# Using cluster mode
apiVersion: v1
kind: Service
metadata:
name: gitea-web
namespace: gitea
labels:
app: gitea-web
spec:
ports:
- port: 80
targetPort: 3000
name: http
selector:
app: gitea
---
# Using node-port mode
# This mainly open a specific TCP port for SSH usage on each host,
# so you can use a proxy layer to handle it(e.g. slb, nginx)
apiVersion: v1
kind: Service
metadata:
name: gitea-ssh
namespace: gitea
labels:
app: gitea-ssh
spec:
ports:
- port: 22
targetPort: 22
nodePort: 30022
name: ssh
selector:
app: gitea
type: NodePort
---
# Ingress is always suitable for HTTP usage,
# we suggest using an proxy layer such as slb to send traffic to different ports.
# Usually 80/443 for web and 22 directly for SSH.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: gitea
namespace: gitea
spec:
rules:
- host: your-gitea-host.com
http:
paths:
- backend:
serviceName: gitea-web
servicePort: 80

View File

@@ -1,265 +0,0 @@
package main
/*
Checkout a PR and load the tests data into sqlite database
*/
import (
"context"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"os"
"os/exec"
"os/user"
"path"
"path/filepath"
"runtime"
"strconv"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/external"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/routers"
"code.gitea.io/gitea/routers/routes"
context2 "github.com/gorilla/context"
"github.com/unknwon/com"
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/testfixtures.v2"
"xorm.io/xorm"
)
var codeFilePath = "contrib/pr/checkout.go"
func runPR() {
log.Printf("[PR] Starting gitea ...\n")
curDir, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
setting.SetCustomPathAndConf("", "", "")
setting.NewContext()
setting.RepoRootPath, err = ioutil.TempDir(os.TempDir(), "repos")
if err != nil {
log.Fatalf("TempDir: %v\n", err)
}
setting.AppDataPath, err = ioutil.TempDir(os.TempDir(), "appdata")
if err != nil {
log.Fatalf("TempDir: %v\n", err)
}
setting.AppWorkPath = curDir
setting.StaticRootPath = curDir
setting.GravatarSourceURL, err = url.Parse("https://secure.gravatar.com/avatar/")
if err != nil {
log.Fatalf("url.Parse: %v\n", err)
}
setting.AppURL = "http://localhost:8080/"
setting.HTTPPort = "8080"
setting.SSH.Domain = "localhost"
setting.SSH.Port = 3000
setting.InstallLock = true
setting.SecretKey = "9pCviYTWSb"
setting.InternalToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTI3OTU5ODN9.OQkH5UmzID2XBdwQ9TAI6Jj2t1X-wElVTjbE7aoN4I8"
curUser, err := user.Current()
if err != nil {
log.Fatal(err)
}
setting.RunUser = curUser.Username
log.Printf("[PR] Loading fixtures data ...\n")
setting.CheckLFSVersion()
//models.LoadConfigs()
/*
setting.Database.Type = "sqlite3"
setting.Database.Path = ":memory:"
setting.Database.Timeout = 500
*/
db := setting.Cfg.Section("database")
db.NewKey("DB_TYPE", "sqlite3")
db.NewKey("PATH", ":memory:")
routers.NewServices()
setting.Database.LogSQL = true
//x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
var helper testfixtures.Helper = &testfixtures.SQLite{}
models.NewEngine(context.Background(), func(_ *xorm.Engine) error {
return nil
})
models.HasEngine = true
//x.ShowSQL(true)
err = models.InitFixtures(
helper,
path.Join(curDir, "models/fixtures/"),
)
if err != nil {
fmt.Printf("Error initializing test database: %v\n", err)
os.Exit(1)
}
models.LoadFixtures()
os.RemoveAll(setting.RepoRootPath)
os.RemoveAll(models.LocalCopyPath())
com.CopyDir(path.Join(curDir, "integrations/gitea-repositories-meta"), setting.RepoRootPath)
log.Printf("[PR] Setting up router\n")
//routers.GlobalInit()
external.RegisterParsers()
markup.Init()
m := routes.NewMacaron()
routes.RegisterRoutes(m)
log.Printf("[PR] Ready for testing !\n")
log.Printf("[PR] Login with user1, user2, user3, ... with pass: password\n")
/*
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
if setting.LFS.StartServer {
log.Info("LFS server enabled")
}
if setting.EnablePprof {
go func() {
log.Info("Starting pprof server on localhost:6060")
log.Info("%v", http.ListenAndServe("localhost:6060", nil))
}()
}
*/
//Start the server
http.ListenAndServe(":8080", context2.ClearHandler(m))
log.Printf("[PR] Cleaning up ...\n")
/*
if err = os.RemoveAll(setting.Indexer.IssuePath); err != nil {
fmt.Printf("os.RemoveAll: %v\n", err)
os.Exit(1)
}
if err = os.RemoveAll(setting.Indexer.RepoPath); err != nil {
fmt.Printf("Unable to remove repo indexer: %v\n", err)
os.Exit(1)
}
*/
if err = os.RemoveAll(setting.RepoRootPath); err != nil {
log.Fatalf("os.RemoveAll: %v\n", err)
}
if err = os.RemoveAll(setting.AppDataPath); err != nil {
log.Fatalf("os.RemoveAll: %v\n", err)
}
}
func main() {
var runPRFlag = flag.Bool("run", false, "Run the PR code")
flag.Parse()
if *runPRFlag {
runPR()
return
}
// To force checkout (e.g. Windows complains about unclean work tree) set env variable FORCE=true
force, err := strconv.ParseBool(os.Getenv("FORCE"))
if err != nil {
force = false
}
//Otherwise checkout PR
if len(os.Args) != 2 {
log.Fatal("Need only one arg: the PR number")
}
pr := os.Args[1]
codeFilePath = filepath.FromSlash(codeFilePath) //Convert to running OS
//Copy this file if it will not exist in the PR branch
dat, err := ioutil.ReadFile(codeFilePath)
if err != nil {
log.Fatalf("Failed to cache this code file : %v", err)
}
repo, err := git.PlainOpen(".")
if err != nil {
log.Fatalf("Failed to open the repo : %v", err)
}
//Find remote upstream
remotes, err := repo.Remotes()
if err != nil {
log.Fatalf("Failed to list remotes of repo : %v", err)
}
remoteUpstream := "origin" //Default
for _, r := range remotes {
if r.Config().URLs[0] == "https://github.com/go-gitea/gitea" || r.Config().URLs[0] == "git@github.com:go-gitea/gitea.git" { //fetch at index 0
remoteUpstream = r.Config().Name
break
}
}
branch := fmt.Sprintf("pr-%s-%d", pr, time.Now().Unix())
branchRef := plumbing.NewBranchReferenceName(branch)
log.Printf("Fetching PR #%s in %s\n", pr, branch)
if runtime.GOOS == "windows" {
//Use git cli command for windows
runCmd("git", "fetch", remoteUpstream, fmt.Sprintf("pull/%s/head:%s", pr, branch))
} else {
ref := fmt.Sprintf("refs/pull/%s/head:%s", pr, branchRef)
err = repo.Fetch(&git.FetchOptions{
RemoteName: remoteUpstream,
RefSpecs: []config.RefSpec{
config.RefSpec(ref),
},
})
if err != nil {
log.Fatalf("Failed to fetch %s from %s : %v", ref, remoteUpstream, err)
}
}
tree, err := repo.Worktree()
if err != nil {
log.Fatalf("Failed to parse git tree : %v", err)
}
log.Printf("Checkout PR #%s in %s\n", pr, branch)
err = tree.Checkout(&git.CheckoutOptions{
Branch: branchRef,
Force: force,
})
if err != nil {
log.Fatalf("Failed to checkout %s : %v", branch, err)
}
//Copy this file if not exist
if _, err := os.Stat(codeFilePath); os.IsNotExist(err) {
err = os.MkdirAll(filepath.Dir(codeFilePath), 0755)
if err != nil {
log.Fatalf("Failed to duplicate this code file in PR : %v", err)
}
err = ioutil.WriteFile(codeFilePath, dat, 0644)
if err != nil {
log.Fatalf("Failed to duplicate this code file in PR : %v", err)
}
}
time.Sleep(5 * time.Second)
//Start with integration test
runCmd("go", "run", "-tags", "sqlite sqlite_unlock_notify", codeFilePath, "-run")
}
func runCmd(cmd ...string) {
log.Printf("Executing : %s ...\n", cmd)
c := exec.Command(cmd[0], cmd[1:]...)
c.Stdout = os.Stdout
c.Stderr = os.Stderr
if err := c.Start(); err != nil {
log.Panicln(err)
}
if err := c.Wait(); err != nil {
log.Panicln(err)
}
}

View File

@@ -1,68 +0,0 @@
[Unit]
Description=Gitea (Git with a cup of tea)
After=syslog.target
After=network.target
###
# Don't forget to add the database service requirements
###
#
#Requires=mysql.service
#Requires=mariadb.service
#Requires=postgresql.service
#Requires=memcached.service
#Requires=redis.service
#
###
# If using socket activation for main http/s
###
#
#After=gitea.main.socket
#Requires=gitea.main.socket
#
###
# (You can also provide gitea an http fallback and/or ssh socket too)
#
# An example of /etc/systemd/system/gitea.main.socket
###
##
## [Unit]
## Description=Gitea Web Socket
## PartOf=gitea.service
##
## [Socket]
## Service=gitea.service
## ListenStream=<some_port>
## NoDelay=true
##
## [Install]
## WantedBy=sockets.target
##
###
[Service]
# Modify these two values and uncomment them if you have
# repos with lots of files and get an HTTP error 500 because
# of that
###
#LimitMEMLOCK=infinity
#LimitNOFILE=65535
RestartSec=2s
Type=simple
User=git
Group=git
WorkingDirectory=/var/lib/gitea/
# If using Unix socket: tells systemd to create the /run/gitea folder, which will contain the gitea.sock file
# (manually creating /run/gitea doesn't work, because it would not persist across reboots)
#RuntimeDirectory=gitea
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
Restart=always
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
# If you want to bind Gitea to a port below 1024, uncomment
# the two values below, or use socket activation to pass Gitea its ports as above
###
#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
#AmbientCapabilities=CAP_NET_BIND_SERVICE
###
[Install]
WantedBy=multi-user.target

View File

@@ -1,964 +0,0 @@
; This file lists the default values used by Gitea
; Copy required sections to your own app.ini (default is custom/conf/app.ini)
; and modify as needed.
; see https://docs.gitea.io/en-us/config-cheat-sheet/ for additional documentation.
; App name that shows in every page title
APP_NAME = Gitea: Git with a cup of tea
; Change it if you run locally
RUN_USER = git
; Either "dev", "prod" or "test", default is "dev"
RUN_MODE = dev
[repository]
ROOT =
SCRIPT_TYPE = bash
; Default ANSI charset
ANSI_CHARSET =
; Force every new repository to be private
FORCE_PRIVATE = false
; Default privacy setting when creating a new repository, allowed values: last, private, public. Default is last which means the last setting used.
DEFAULT_PRIVATE = last
; Global limit of repositories per user, applied at creation time. -1 means no limit
MAX_CREATION_LIMIT = -1
; Mirror sync queue length, increase if mirror syncing starts hanging
MIRROR_QUEUE_LENGTH = 1000
; Patch test queue length, increase if pull request patch testing starts hanging
PULL_REQUEST_QUEUE_LENGTH = 1000
; Preferred Licenses to place at the top of the List
; The name here must match the filename in conf/license or custom/conf/license
PREFERRED_LICENSES = Apache License 2.0,MIT License
; Disable the ability to interact with repositories using the HTTP protocol
DISABLE_HTTP_GIT = false
; Value for Access-Control-Allow-Origin header, default is not to present
; WARNING: This maybe harmful to you website if you do not give it a right value.
ACCESS_CONTROL_ALLOW_ORIGIN =
; Force ssh:// clone url instead of scp-style uri when default SSH port is used
USE_COMPAT_SSH_URI = false
; Close issues as long as a commit on any branch marks it as fixed
DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH = false
; Allow users to push local repositories to Gitea and have them automatically created for a user or an org
ENABLE_PUSH_CREATE_USER = false
ENABLE_PUSH_CREATE_ORG = false
[repository.editor]
; List of file extensions for which lines should be wrapped in the CodeMirror editor
; Separate extensions with a comma. To line wrap files without an extension, just put a comma
LINE_WRAP_EXTENSIONS = .txt,.md,.markdown,.mdown,.mkd,
; Valid file modes that have a preview API associated with them, such as api/v1/markdown
; Separate the values by commas. The preview tab in edit mode won't be displayed if the file extension doesn't match
PREVIEWABLE_FILE_MODES = markdown
[repository.local]
; Path for local repository copy. Defaults to `tmp/local-repo`
LOCAL_COPY_PATH = tmp/local-repo
; Path for local wiki copy. Defaults to `tmp/local-wiki`
LOCAL_WIKI_PATH = tmp/local-wiki
[repository.upload]
; Whether repository file uploads are enabled. Defaults to `true`
ENABLED = true
; Path for uploads. Defaults to `data/tmp/uploads` (tmp gets deleted on gitea restart)
TEMP_PATH = data/tmp/uploads
; One or more allowed types, e.g. image/jpeg|image/png. Nothing means any file type
ALLOWED_TYPES =
; Max size of each file in megabytes. Defaults to 3MB
FILE_MAX_SIZE = 3
; Max number of files per upload. Defaults to 5
MAX_FILES = 5
[repository.pull-request]
; List of prefixes used in Pull Request title to mark them as Work In Progress
WORK_IN_PROGRESS_PREFIXES=WIP:,[WIP]
; List of keywords used in Pull Request comments to automatically close a related issue
CLOSE_KEYWORDS=close,closes,closed,fix,fixes,fixed,resolve,resolves,resolved
; List of keywords used in Pull Request comments to automatically reopen a related issue
REOPEN_KEYWORDS=reopen,reopens,reopened
; In the default merge message for squash commits include at most this many commits
DEFAULT_MERGE_MESSAGE_COMMITS_LIMIT=50
; In the default merge message for squash commits limit the size of the commit messages to this
DEFAULT_MERGE_MESSAGE_SIZE=5120
; In the default merge message for squash commits walk all commits to include all authors in the Co-authored-by otherwise just use those in the limited list
DEFAULT_MERGE_MESSAGE_ALL_AUTHORS=false
; In default merge messages limit the number of approvers listed as Reviewed-by: to this many
DEFAULT_MERGE_MESSAGE_MAX_APPROVERS=10
; In default merge messages only include approvers who are official
DEFAULT_MERGE_MESSAGE_OFFICIAL_APPROVERS_ONLY=true
[repository.issue]
; List of reasons why a Pull Request or Issue can be locked
LOCK_REASONS=Too heated,Off-topic,Resolved,Spam
[repository.signing]
; GPG key to use to sign commits, Defaults to the default - that is the value of git config --get user.signingkey
; run in the context of the RUN_USER
; Switch to none to stop signing completely
SIGNING_KEY = default
; If a SIGNING_KEY ID is provided and is not set to default, use the provided Name and Email address as the signer.
; These should match a publicized name and email address for the key. (When SIGNING_KEY is default these are set to
; the results of git config --get user.name and git config --get user.email respectively and can only be overrided
; by setting the SIGNING_KEY ID to the correct ID.)
SIGNING_NAME =
SIGNING_EMAIL =
; Determines when gitea should sign the initial commit when creating a repository
; Either:
; - never
; - pubkey: only sign if the user has a pubkey
; - twofa: only sign if the user has logged in with twofa
; - always
; options other than none and always can be combined as comma separated list
INITIAL_COMMIT = always
; Determines when to sign for CRUD actions
; - as above
; - parentsigned: requires that the parent commit is signed.
CRUD_ACTIONS = pubkey, twofa, parentsigned
; Determines when to sign Wiki commits
; - as above
WIKI = never
; Determines when to sign on merges
; - basesigned: require that the parent of commit on the base repo is signed.
; - commitssigned: require that all the commits in the head branch are signed.
; - approved: only sign when merging an approved pr to a protected branch
MERGES = pubkey, twofa, basesigned, commitssigned
[cors]
; More information about CORS can be found here: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#The_HTTP_response_headers
; enable cors headers (disabled by default)
ENABLED=false
; scheme of allowed requests
SCHEME=http
; list of requesting domains that are allowed
ALLOW_DOMAIN=*
; allow subdomains of headers listed above to request
ALLOW_SUBDOMAIN=false
; list of methods allowed to request
METHODS=GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS
; max time to cache response
MAX_AGE=10m
; allow request with credentials
ALLOW_CREDENTIALS=false
[ui]
; Number of repositories that are displayed on one explore page
EXPLORE_PAGING_NUM = 20
; Number of issues that are displayed on one page
ISSUE_PAGING_NUM = 10
; Number of maximum commits displayed in one activity feed
FEED_MAX_COMMIT_NUM = 5
; Number of maximum commits displayed in commit graph.
GRAPH_MAX_COMMIT_NUM = 100
; Number of line of codes shown for a code comment
CODE_COMMENT_LINES = 4
; Value of `theme-color` meta tag, used by Android >= 5.0
; An invalid color like "none" or "disable" will have the default style
; More info: https://developers.google.com/web/updates/2014/11/Support-for-theme-color-in-Chrome-39-for-Android
THEME_COLOR_META_TAG = `#6cc644`
; Max size of files to be displayed (default is 8MiB)
MAX_DISPLAY_FILE_SIZE = 8388608
; Whether the email of the user should be shown in the Explore Users page
SHOW_USER_EMAIL = true
; Set the default theme for the Gitea install
DEFAULT_THEME = gitea
; All available themes. Allow users select personalized themes regardless of the value of `DEFAULT_THEME`.
THEMES = gitea,arc-green
; All available reactions. Allow users react with different emoji's
; For the whole list look at https://gitea.com/gitea/gitea.com/issues/8
REACTIONS = +1, -1, laugh, hooray, confused, heart, rocket, eyes
; Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used.
DEFAULT_SHOW_FULL_NAME = false
; Whether to search within description at repository search on explore page.
SEARCH_REPO_DESCRIPTION = true
; Whether to enable a Service Worker to cache frontend assets
USE_SERVICE_WORKER = true
[ui.admin]
; Number of users that are displayed on one page
USER_PAGING_NUM = 50
; Number of repos that are displayed on one page
REPO_PAGING_NUM = 50
; Number of notices that are displayed on one page
NOTICE_PAGING_NUM = 25
; Number of organizations that are displayed on one page
ORG_PAGING_NUM = 50
[ui.user]
; Number of repos that are displayed on one page
REPO_PAGING_NUM = 15
[ui.meta]
AUTHOR = Gitea - Git with a cup of tea
DESCRIPTION = Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go
KEYWORDS = go,git,self-hosted,gitea
[markdown]
; Enable hard line break extension
ENABLE_HARD_LINE_BREAK = false
; Comma separated list of custom URL-Schemes that are allowed as links when rendering Markdown
; for example git,magnet,ftp (more at https://en.wikipedia.org/wiki/List_of_URI_schemes)
; URLs starting with http and https are always displayed, whatever is put in this entry.
CUSTOM_URL_SCHEMES =
; List of file extensions that should be rendered/edited as Markdown
; Separate the extensions with a comma. To render files without any extension as markdown, just put a comma
FILE_EXTENSIONS = .md,.markdown,.mdown,.mkd
[server]
; The protocol the server listens on. One of 'http', 'https', 'unix' or 'fcgi'.
PROTOCOL = http
DOMAIN = localhost
ROOT_URL = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
; when STATIC_URL_PREFIX is empty it will follow APP_URL
STATIC_URL_PREFIX =
; The address to listen on. Either a IPv4/IPv6 address or the path to a unix socket.
HTTP_ADDR = 0.0.0.0
HTTP_PORT = 3000
; If REDIRECT_OTHER_PORT is true, and PROTOCOL is set to https an http server
; will be started on PORT_TO_REDIRECT and it will redirect plain, non-secure http requests to the main
; ROOT_URL. Defaults are false for REDIRECT_OTHER_PORT and 80 for
; PORT_TO_REDIRECT.
REDIRECT_OTHER_PORT = false
PORT_TO_REDIRECT = 80
; Permission for unix socket
UNIX_SOCKET_PERMISSION = 666
; Local (DMZ) URL for Gitea workers (such as SSH update) accessing web service.
; In 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 'unix'.
LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
; 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. If blank, then it is the value of RUN_USER.
BUILTIN_SSH_SERVER_USER =
; Domain name to be exposed in clone URL
SSH_DOMAIN = %(DOMAIN)s
; The network interface the builtin SSH server should listen on
SSH_LISTEN_HOST =
; Port number to be exposed in clone URL
SSH_PORT = 22
; The port number the builtin SSH server should listen on
SSH_LISTEN_PORT = %(SSH_PORT)s
; Root path of SSH directory, default is '~/.ssh', but you have to use '/home/git/.ssh'.
SSH_ROOT_PATH =
; Gitea will create a authorized_keys file by default when it is not using the internal ssh server
; If you intend to use the AuthorizedKeysCommand functionality then you should turn this off.
SSH_CREATE_AUTHORIZED_KEYS_FILE = true
; For the built-in SSH server, choose the ciphers to support for SSH connections,
; for system SSH this setting has no effect
SSH_SERVER_CIPHERS = aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, arcfour256, arcfour128
; For the built-in SSH server, choose the key exchange algorithms to support for SSH connections,
; for system SSH this setting has no effect
SSH_SERVER_KEY_EXCHANGES = diffie-hellman-group1-sha1, diffie-hellman-group14-sha1, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, curve25519-sha256@libssh.org
; For the built-in SSH server, choose the MACs to support for SSH connections,
; for system SSH this setting has no effect
SSH_SERVER_MACS = hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1, hmac-sha1-96
; Directory to create temporary files in when testing public keys using ssh-keygen,
; default is the system temporary directory.
SSH_KEY_TEST_PATH =
; Path to ssh-keygen, default is 'ssh-keygen' which means the shell is responsible for finding out which one to call.
SSH_KEYGEN_PATH = ssh-keygen
; Enable SSH Authorized Key Backup when rewriting all keys, default is true
SSH_BACKUP_AUTHORIZED_KEYS = true
; Enable exposure of SSH clone URL to anonymous visitors, default is false
SSH_EXPOSE_ANONYMOUS = false
; Indicate whether to check minimum key size with corresponding type
MINIMUM_KEY_SIZE_CHECK = false
; Disable CDN even in "prod" mode
OFFLINE_MODE = false
DISABLE_ROUTER_LOG = false
; Generate steps:
; $ ./gitea cert -ca=true -duration=8760h0m0s -host=myhost.example.com
;
; Or from a .pfx file exported from the Windows certificate store (do
; not forget to export the private key):
; $ openssl pkcs12 -in cert.pfx -out cert.pem -nokeys
; $ openssl pkcs12 -in cert.pfx -out key.pem -nocerts -nodes
CERT_FILE = custom/https/cert.pem
KEY_FILE = custom/https/key.pem
; Root directory containing templates and static files.
; default is the path where Gitea is executed
STATIC_ROOT_PATH =
; Default path for App data
APP_DATA_PATH = data
; Application level GZIP support
ENABLE_GZIP = false
; Application profiling (memory and cpu)
; For "web" command it listens on localhost:6060
; For "serve" command it dumps to disk at PPROF_DATA_PATH as (cpuprofile|memprofile)_<username>_<temporary id>
ENABLE_PPROF = false
; PPROF_DATA_PATH, use an absolute path when you start gitea as service
PPROF_DATA_PATH = data/tmp/pprof
; Landing page, can be "home", "explore", "organizations" or "login"
; The "login" choice is not a security measure but just a UI flow change, use REQUIRE_SIGNIN_VIEW to force users to log in.
LANDING_PAGE = home
; Enables git-lfs support. true or false, default is false.
LFS_START_SERVER = false
; Where your lfs files reside, default is data/lfs.
LFS_CONTENT_PATH = data/lfs
; LFS authentication secret, change this yourself
LFS_JWT_SECRET =
; LFS authentication validity period (in time.Duration), pushes taking longer than this may fail.
LFS_HTTP_AUTH_EXPIRY = 20m
; Allow graceful restarts using SIGHUP to fork
ALLOW_GRACEFUL_RESTARTS = true
; After a restart the parent will finish ongoing requests before
; shutting down. Force shutdown if this process takes longer than this delay.
; set to a negative value to disable
GRACEFUL_HAMMER_TIME = 60s
; Allows the setting of a startup timeout and waithint for Windows as SVC service
; 0 disables this.
STARTUP_TIMEOUT = 0
; Static resources, includes resources on custom/, public/ and all uploaded avatars web browser cache time, default is 6h
STATIC_CACHE_TIME = 6h
; Define allowed algorithms and their minimum key length (use -1 to disable a type)
[ssh.minimum_key_sizes]
ED25519 = 256
ECDSA = 256
RSA = 2048
DSA = 1024
[database]
; Either "mysql", "postgres", "mssql" or "sqlite3", it's your choice
DB_TYPE = mysql
HOST = 127.0.0.1:3306
NAME = gitea
USER = root
; Use PASSWD = `your password` for quoting if you use special characters in the password.
PASSWD =
; For Postgres, either "disable" (default), "require", or "verify-full"
; For MySQL, either "false" (default), "true", or "skip-verify"
SSL_MODE = disable
; For MySQL only, either "utf8" or "utf8mb4", default is "utf8".
; NOTICE: for "utf8mb4" you must use MySQL InnoDB > 5.6. Gitea is unable to check this.
CHARSET = utf8
; For "sqlite3" and "tidb", use an absolute path when you start gitea as service
PATH = data/gitea.db
; For "sqlite3" only. Query timeout
SQLITE_TIMEOUT = 500
; For iterate buffer, default is 50
ITERATE_BUFFER_SIZE = 50
; Show the database generated SQL
LOG_SQL = true
; Maximum number of DB Connect retries
DB_RETRIES = 10
; Backoff time per DB retry (time.Duration)
DB_RETRY_BACKOFF = 3s
; Max idle database connections on connnection pool, default is 2
MAX_IDLE_CONNS = 2
; Database connection max life time, default is 0 or 3s mysql (See #6804 & #7071 for reasoning)
CONN_MAX_LIFETIME = 3s
; Database maximum number of open connections, default is 0 meaning no maximum
MAX_OPEN_CONNS = 0
[indexer]
; Issue indexer type, currently support: bleve or db, default is bleve
ISSUE_INDEXER_TYPE = bleve
; Issue indexer storage path, available when ISSUE_INDEXER_TYPE is bleve
ISSUE_INDEXER_PATH = indexers/issues.bleve
; Issue indexer queue, currently support: channel, levelqueue or redis, default is levelqueue
ISSUE_INDEXER_QUEUE_TYPE = levelqueue
; When ISSUE_INDEXER_QUEUE_TYPE is levelqueue, this will be the queue will be saved path,
; default is indexers/issues.queue
ISSUE_INDEXER_QUEUE_DIR = indexers/issues.queue
; When `ISSUE_INDEXER_QUEUE_TYPE` is `redis`, this will store the redis connection string.
ISSUE_INDEXER_QUEUE_CONN_STR = "addrs=127.0.0.1:6379 db=0"
; Batch queue number, default is 20
ISSUE_INDEXER_QUEUE_BATCH_NUMBER = 20
; Timeout the indexer if it takes longer than this to start.
; Set to zero to disable timeout.
STARTUP_TIMEOUT=30s
; repo indexer by default disabled, since it uses a lot of disk space
REPO_INDEXER_ENABLED = false
REPO_INDEXER_PATH = indexers/repos.bleve
UPDATE_BUFFER_LEN = 20
MAX_FILE_SIZE = 1048576
; A comma separated list of glob patterns (see https://github.com/gobwas/glob) to include
; in the index; default is empty
REPO_INDEXER_INCLUDE =
; A comma separated list of glob patterns to exclude from the index; ; default is empty
REPO_INDEXER_EXCLUDE =
[queue]
; Specific queues can be individually configured with [queue.name]. [queue] provides defaults
;
; General queue queue type, currently support: persistable-channel, channel, level, redis, dummy
; default to persistable-channel
TYPE = persistable-channel
; data-dir for storing persistable queues and level queues, individual queues will be named by their type
DATADIR = queues/
; Default queue length before a channel queue will block
LENGTH = 20
; Batch size to send for batched queues
BATCH_LENGTH = 20
; Connection string for redis queues this will store the redis connection string.
CONN_STR = "addrs=127.0.0.1:6379 db=0"
; Provide the suffix of the default redis queue name - specific queues can be overriden within in their [queue.name] sections.
QUEUE_NAME = "_queue"
; If the queue cannot be created at startup - level queues may need a timeout at startup - wrap the queue:
WRAP_IF_NECESSARY = true
; Attempt to create the wrapped queue at max
MAX_ATTEMPTS = 10
; Timeout queue creation
TIMEOUT = 15m30s
; Create a pool with this many workers
WORKERS = 1
; Dynamically scale the worker pool to at this many workers
MAX_WORKERS = 10
; Add boost workers when the queue blocks for BLOCK_TIMEOUT
BLOCK_TIMEOUT = 1s
; Remove the boost workers after BOOST_TIMEOUT
BOOST_TIMEOUT = 5m
; During a boost add BOOST_WORKERS
BOOST_WORKERS = 5
[admin]
; Disallow regular (non-admin) users from creating organizations.
DISABLE_REGULAR_ORG_CREATION = false
; Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled
DEFAULT_EMAIL_NOTIFICATIONS = enabled
[security]
; Whether the installer is disabled
INSTALL_LOCK = false
; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!!
SECRET_KEY = !#@FDEWREWR&*(
; How long to remember that a user is logged in before requiring relogin (in days)
LOGIN_REMEMBER_DAYS = 7
COOKIE_USERNAME = gitea_awesome
COOKIE_REMEMBER_NAME = gitea_incredible
; Reverse proxy authentication header name of user name
REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
REVERSE_PROXY_AUTHENTICATION_EMAIL = X-WEBAUTH-EMAIL
; The minimum password length for new Users
MIN_PASSWORD_LENGTH = 6
; Set to true to allow users to import local server paths
IMPORT_LOCAL_PATHS = false
; Set to true to prevent all users (including admin) from creating custom git hooks
DISABLE_GIT_HOOKS = false
; Set to false to allow pushes to gitea repositories despite having an incomplete environment - NOT RECOMMENDED
ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET = true
;Comma separated list of character classes required to pass minimum complexity.
;If left empty or no valid values are specified, the default values ("lower,upper,digit,spec") will be used.
;Use "off" to disable checking.
PASSWORD_COMPLEXITY = lower,upper,digit,spec
; Password Hash algorithm, either "pbkdf2", "argon2", "scrypt" or "bcrypt"
PASSWORD_HASH_ALGO = pbkdf2
; Set false to allow JavaScript to read CSRF cookie
CSRF_COOKIE_HTTP_ONLY = true
[openid]
;
; OpenID is an open, standard and decentralized authentication protocol.
; Your identity is the address of a webpage you provide, which describes
; how to prove you are in control of that page.
;
; For more info: https://en.wikipedia.org/wiki/OpenID
;
; Current implementation supports OpenID-2.0
;
; Tested to work providers at the time of writing:
; - Any GNUSocial node (your.hostname.tld/username)
; - Any SimpleID provider (http://simpleid.koinic.net)
; - http://openid.org.cn/
; - openid.stackexchange.com
; - login.launchpad.net
; - <username>.livejournal.com
;
; Whether to allow signin in via OpenID
ENABLE_OPENID_SIGNIN = true
; Whether to allow registering via OpenID
; Do not include to rely on rhw DISABLE_REGISTRATION setting
;ENABLE_OPENID_SIGNUP = true
; Allowed URI patterns (POSIX regexp).
; Space separated.
; Only these would be allowed if non-blank.
; Example value: trusted.domain.org trusted.domain.net
WHITELISTED_URIS =
; Forbidden URI patterns (POSIX regexp).
; Space separated.
; Only used if WHITELISTED_URIS is blank.
; Example value: loadaverage.org/badguy stackexchange.com/.*spammer
BLACKLISTED_URIS =
[service]
; Time limit to confirm account/email registration
ACTIVE_CODE_LIVE_MINUTES = 180
; Time limit to perform the reset of a forgotten password
RESET_PASSWD_CODE_LIVE_MINUTES = 180
; Whether a new user needs to confirm their email when registering.
REGISTER_EMAIL_CONFIRM = false
; List of domain names that are allowed to be used to register on a Gitea instance
; gitea.io,example.com
EMAIL_DOMAIN_WHITELIST=
; Disallow registration, only allow admins to create accounts.
DISABLE_REGISTRATION = false
; Allow registration only using third-party services, it works only when DISABLE_REGISTRATION is false
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
; User must sign in to view anything.
REQUIRE_SIGNIN_VIEW = false
; Mail notification
ENABLE_NOTIFY_MAIL = false
; This setting enables gitea to be signed in with HTTP BASIC Authentication using the user's password
; If you set this to false you will not be able to access the tokens endpoints on the API with your password
; Please note that setting this to false will not disable OAuth Basic or Basic authentication using a token
ENABLE_BASIC_AUTHENTICATION = true
; More detail: https://github.com/gogits/gogs/issues/165
ENABLE_REVERSE_PROXY_AUTHENTICATION = false
ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false
ENABLE_REVERSE_PROXY_EMAIL = false
; Enable captcha validation for registration
ENABLE_CAPTCHA = false
; Type of captcha you want to use. Options: image, recaptcha
CAPTCHA_TYPE = image
; Enable recaptcha to use Google's recaptcha service
; Go to https://www.google.com/recaptcha/admin to sign up for a key
RECAPTCHA_SECRET =
RECAPTCHA_SITEKEY =
; Change this to use recaptcha.net or other recaptcha service
RECAPTCHA_URL = https://www.google.com/recaptcha/
; Default value for KeepEmailPrivate
; Each new user will get the value of this setting copied into their profile
DEFAULT_KEEP_EMAIL_PRIVATE = false
; Default value for AllowCreateOrganization
; Every new user will have rights set to create organizations depending on this setting
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
; Either "public", "limited" or "private", default is "public"
; Limited is for signed user only
; Private is only for member of the organization
; Public is for everyone
DEFAULT_ORG_VISIBILITY = public
; Default value for DefaultOrgMemberVisible
; True will make the membership of the users visible when added to the organisation
DEFAULT_ORG_MEMBER_VISIBLE = false
; Default value for EnableDependencies
; Repositories will use dependencies by default depending on this setting
DEFAULT_ENABLE_DEPENDENCIES = true
; Dependencies can be added from any repository where the user is granted access or only from the current repository depending on this setting.
ALLOW_CROSS_REPOSITORY_DEPENDENCIES = true
; Enable heatmap on users profiles.
ENABLE_USER_HEATMAP = true
; Enable Timetracking
ENABLE_TIMETRACKING = true
; Default value for EnableTimetracking
; Repositories will use timetracking by default depending on this setting
DEFAULT_ENABLE_TIMETRACKING = true
; Default value for AllowOnlyContributorsToTrackTime
; Only users with write permissions can track time if this is true
DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME = true
; Default value for the domain part of the user's email address in the git log
; if he has set KeepEmailPrivate to true. The user's email will be replaced with a
; concatenation of the user name in lower case, "@" and NO_REPLY_ADDRESS.
NO_REPLY_ADDRESS = noreply.%(DOMAIN)s
; Show Registration button
SHOW_REGISTRATION_BUTTON = true
; Show milestones dashboard page - a view of all the user's milestones
SHOW_MILESTONES_DASHBOARD_PAGE = true
; Default value for AutoWatchNewRepos
; When adding a repo to a team or creating a new repo all team members will watch the
; repo automatically if enabled
AUTO_WATCH_NEW_REPOS = true
; Default value for AutoWatchOnChanges
; Make the user watch a repository When they commit for the first time
AUTO_WATCH_ON_CHANGES = false
[webhook]
; Hook task queue length, increase if webhook shooting starts hanging
QUEUE_LENGTH = 1000
; Deliver timeout in seconds
DELIVER_TIMEOUT = 5
; Allow insecure certification
SKIP_TLS_VERIFY = false
; Number of history information in each page
PAGING_NUM = 10
; Proxy server URL, support http://, https//, socks://, blank will follow environment http_proxy/https_proxy
PROXY_URL =
; Comma separated list of host names requiring proxy. Glob patterns (*) are accepted; use ** to match all hosts.
PROXY_HOSTS =
[mailer]
ENABLED = false
; Buffer length of channel, keep it as it is if you don't know what it is.
SEND_BUFFER_LEN = 100
; Prefix displayed before subject in mail
SUBJECT_PREFIX =
; Mail server
; Gmail: smtp.gmail.com:587
; QQ: smtp.qq.com:465
; Note, if the port ends with "465", SMTPS will be used. Using STARTTLS on port 587 is recommended per RFC 6409. If the server supports STARTTLS it will always be used.
HOST =
; Disable HELO operation when hostnames are different.
DISABLE_HELO =
; Custom hostname for HELO operation, if no value is provided, one is retrieved from system.
HELO_HOSTNAME =
; Do not verify the certificate of the server. Only use this for self-signed certificates
SKIP_VERIFY =
; Use client certificate
USE_CERTIFICATE = false
CERT_FILE = custom/mailer/cert.pem
KEY_FILE = custom/mailer/key.pem
; Should SMTP connection use TLS
IS_TLS_ENABLED = false
; Mail from address, RFC 5322. This can be just an email address, or the `"Name" <email@example.com>` format
FROM =
; Mailer user name and password
USER =
; Use PASSWD = `your password` for quoting if you use special characters in the password.
PASSWD =
; Send mails as plain text
SEND_AS_PLAIN_TEXT = false
; Set Mailer Type (either SMTP, sendmail or dummy to just send to the log)
MAILER_TYPE = smtp
; Specify an alternative sendmail binary
SENDMAIL_PATH = sendmail
; Specify any extra sendmail arguments
SENDMAIL_ARGS =
[cache]
; Either "memory", "redis", or "memcache", default is "memory"
ADAPTER = memory
; For "memory" only, GC interval in seconds, default is 60
INTERVAL = 60
; For "redis" and "memcache", connection host address
; redis: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180
; memcache: `127.0.0.1:11211`
HOST =
; Time to keep items in cache if not used, default is 16 hours.
; Setting it to 0 disables caching
ITEM_TTL = 16h
[session]
; Either "memory", "file", or "redis", default is "memory"
PROVIDER = memory
; Provider config options
; memory: doesn't have any config yet
; file: session file path, e.g. `data/sessions`
; redis: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180
; mysql: go-sql-driver/mysql dsn config string, e.g. `root:password@/session_table`
PROVIDER_CONFIG = data/sessions
; Session cookie name
COOKIE_NAME = i_like_gitea
; If you use session in https only, default is false
COOKIE_SECURE = false
; Enable set cookie, default is true
ENABLE_SET_COOKIE = true
; Session GC time interval in seconds, default is 86400 (1 day)
GC_INTERVAL_TIME = 86400
; Session life time in seconds, default is 86400 (1 day)
SESSION_LIFE_TIME = 86400
[picture]
AVATAR_UPLOAD_PATH = data/avatars
REPOSITORY_AVATAR_UPLOAD_PATH = data/repo-avatars
; How Gitea deals with missing repository avatars
; none = no avatar will be displayed; random = random avatar will be displayed; image = default image will be used
REPOSITORY_AVATAR_FALLBACK = none
REPOSITORY_AVATAR_FALLBACK_IMAGE = /img/repo_default.png
; Max Width and Height of uploaded avatars.
; This is to limit the amount of RAM used when resizing the image.
AVATAR_MAX_WIDTH = 4096
AVATAR_MAX_HEIGHT = 3072
; Maximum alloved file size for uploaded avatars.
; This is to limit the amount of RAM used when resizing the image.
AVATAR_MAX_FILE_SIZE = 1048576
; Chinese users can choose "duoshuo"
; or a custom avatar source, like: http://cn.gravatar.com/avatar/
GRAVATAR_SOURCE = gravatar
; This value will always be true in offline mode.
DISABLE_GRAVATAR = false
; Federated avatar lookup uses DNS to discover avatar associated
; with emails, see https://www.libravatar.org
; This value will always be false in offline mode or when Gravatar is disabled.
ENABLE_FEDERATED_AVATAR = false
[attachment]
; Whether attachments are enabled. Defaults to `true`
ENABLED = true
; Path for attachments. Defaults to `data/attachments`
PATH = data/attachments
; One or more allowed types, e.g. image/jpeg|image/png
ALLOWED_TYPES = image/jpeg|image/png|application/zip|application/gzip
; Max size of each file. Defaults to 4MB
MAX_SIZE = 4
; Max number of files per upload. Defaults to 5
MAX_FILES = 5
[time]
; Specifies the format for fully outputted dates. Defaults to RFC1123
; Special supported values are ANSIC, UnixDate, RubyDate, RFC822, RFC822Z, RFC850, RFC1123, RFC1123Z, RFC3339, RFC3339Nano, Kitchen, Stamp, StampMilli, StampMicro and StampNano
; For more information about the format see http://golang.org/pkg/time/#pkg-constants
FORMAT =
; Location the UI time display i.e. Asia/Shanghai
; Empty means server's location setting
DEFAULT_UI_LOCATION =
[log]
ROOT_PATH =
; Either "console", "file", "conn", "smtp" or "database", default is "console"
; Use comma to separate multiple modes, e.g. "console, file"
MODE = console
; Buffer length of the channel, keep it as it is if you don't know what it is.
BUFFER_LEN = 10000
REDIRECT_MACARON_LOG = false
MACARON = file
; Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Info"
ROUTER_LOG_LEVEL = Info
ROUTER = console
ENABLE_ACCESS_LOG = false
ACCESS_LOG_TEMPLATE = {{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"
ACCESS = file
; Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace"
LEVEL = Info
; Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "None"
STACKTRACE_LEVEL = None
; Generic log modes
[log.x]
FLAGS = stdflags
EXPRESSION =
PREFIX =
COLORIZE = false
; For "console" mode only
[log.console]
LEVEL =
STDERR = false
; For "file" mode only
[log.file]
LEVEL =
; Set the file_name for the logger. If this is a relative path this
; will be relative to ROOT_PATH
FILE_NAME =
; This enables automated log rotate(switch of following options), default is true
LOG_ROTATE = true
; Max number of lines in a single file, default is 1000000
MAX_LINES = 1000000
; Max size shift of a single file, default is 28 means 1 << 28, 256MB
MAX_SIZE_SHIFT = 28
; Segment log daily, default is true
DAILY_ROTATE = true
; delete the log file after n days, default is 7
MAX_DAYS = 7
; compress logs with gzip
COMPRESS = true
; compression level see godoc for compress/gzip
COMPRESSION_LEVEL = -1
; For "conn" mode only
[log.conn]
LEVEL =
; Reconnect host for every single message, default is false
RECONNECT_ON_MSG = false
; Try to reconnect when connection is lost, default is false
RECONNECT = false
; Either "tcp", "unix" or "udp", default is "tcp"
PROTOCOL = tcp
; Host address
ADDR =
; For "smtp" mode only
[log.smtp]
LEVEL =
; Name displayed in mail title, default is "Diagnostic message from server"
SUBJECT = Diagnostic message from server
; Mail server
HOST =
; Mailer user name and password
USER =
; Use PASSWD = `your password` for quoting if you use special characters in the password.
PASSWD =
; Receivers, can be one or more, e.g. 1@example.com,2@example.com
RECEIVERS =
[cron]
; Enable running cron tasks periodically.
ENABLED = true
; Run cron tasks when Gitea starts.
RUN_AT_START = false
; Update mirrors
[cron.update_mirrors]
SCHEDULE = @every 10m
; Repository health check
[cron.repo_health_check]
SCHEDULE = @every 24h
TIMEOUT = 60s
; Arguments for command 'git fsck', e.g. "--unreachable --tags"
; see more on http://git-scm.com/docs/git-fsck
ARGS =
; Check repository statistics
[cron.check_repo_stats]
RUN_AT_START = true
SCHEDULE = @every 24h
; Clean up old repository archives
[cron.archive_cleanup]
; Whether to enable the job
ENABLED = true
; Whether to always run at least once at start up time (if ENABLED)
RUN_AT_START = true
; Time interval for job to run
SCHEDULE = @every 24h
; Archives created more than OLDER_THAN ago are subject to deletion
OLDER_THAN = 24h
; Synchronize external user data (only LDAP user synchronization is supported)
[cron.sync_external_users]
; Synchronize external user data when starting server (default false)
RUN_AT_START = false
; Interval as a duration between each synchronization (default every 24h)
SCHEDULE = @every 24h
; Create new users, update existing user data and disable users that are not in external source anymore (default)
; or only create new users if UPDATE_EXISTING is set to false
UPDATE_EXISTING = true
; Update migrated repositories' issues and comments' posterid, it will always attempt synchronization when the instance starts.
[cron.update_migration_post_id]
; Interval as a duration between each synchronization. (default every 24h)
SCHEDULE = @every 24h
[git]
; The path of git executable. If empty, Gitea searches through the PATH environment.
PATH =
; Disables highlight of added and removed changes
DISABLE_DIFF_HIGHLIGHT = false
; Max number of lines allowed in a single file in diff view
MAX_GIT_DIFF_LINES = 1000
; Max number of allowed characters in a line in diff view
MAX_GIT_DIFF_LINE_CHARACTERS = 5000
; Max number of files shown in diff view
MAX_GIT_DIFF_FILES = 100
; Arguments for command 'git gc', e.g. "--aggressive --auto"
; see more on http://git-scm.com/docs/git-gc/
GC_ARGS =
; If use git wire protocol version 2 when git version >= 2.18, default is true, set to false when you always want git wire protocol version 1
EnableAutoGitWireProtocol = true
; Operation timeout in seconds
[git.timeout]
DEFAULT = 360
MIGRATE = 600
MIRROR = 300
CLONE = 300
PULL = 300
GC = 60
[mirror]
; Default interval as a duration between each check
DEFAULT_INTERVAL = 8h
; Min interval as a duration must be > 1m
MIN_INTERVAL = 10m
[api]
; Enables Swagger. True or false; default is true.
ENABLE_SWAGGER = true
; Max number of items in a page
MAX_RESPONSE_ITEMS = 50
; Default paging number of api
DEFAULT_PAGING_NUM = 30
; Default and maximum number of items per page for git trees api
DEFAULT_GIT_TREES_PER_PAGE = 1000
; Default size of a blob returned by the blobs API (default is 10MiB)
DEFAULT_MAX_BLOB_SIZE = 10485760
[oauth2]
; Enables OAuth2 provider
ENABLE = true
; Lifetime of an OAuth2 access token in seconds
ACCESS_TOKEN_EXPIRATION_TIME=3600
; Lifetime of an OAuth2 access token in hours
REFRESH_TOKEN_EXPIRATION_TIME=730
; Check if refresh token got already used
INVALIDATE_REFRESH_TOKENS=false
; OAuth2 authentication secret for access and refresh tokens, change this to a unique string.
JWT_SECRET=Bk0yK7Y9g_p56v86KaHqjSbxvNvu3SbKoOdOt2ZcXvU
[i18n]
LANGS = en-US,zh-CN,zh-HK,zh-TW,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,uk-UA,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT,fi-FI,tr-TR,cs-CZ,sr-SP,sv-SE,ko-KR
NAMES = English,简体中文,繁體中文(香港),繁體中文(台灣),Deutsch,français,Nederlands,latviešu,русский,Українська,日本語,español,português do Brasil,polski,български,italiano,suomi,Türkçe,čeština,српски,svenska,한국어
; Used for datetimepicker
[i18n.datelang]
en-US = en
zh-CN = zh
zh-HK = zh-HK
zh-TW = zh-TW
de-DE = de
fr-FR = fr
nl-NL = nl
lv-LV = lv
ru-RU = ru
uk-UA = uk
ja-JP = ja
es-ES = es
pt-BR = pt-BR
pl-PL = pl
bg-BG = bg
it-IT = it
fi-FI = fi
tr-TR = tr
cs-CZ = cs-CZ
sr-SP = sr
sv-SE = sv
ko-KR = ko
[U2F]
; NOTE: THE DEFAULT VALUES HERE WILL NEED TO BE CHANGED
; Two Factor authentication with security keys
; https://developers.yubico.com/U2F/App_ID.html
;APP_ID = http://localhost:3000/
; Comma seperated list of trusted facets
;TRUSTED_FACETS = http://localhost:3000/
; Extension mapping to highlight class
; e.g. .toml=ini
[highlight.mapping]
[other]
SHOW_FOOTER_BRANDING = false
; Show version information about Gitea and Go in the footer
SHOW_FOOTER_VERSION = true
; Show template execution time in the footer
SHOW_FOOTER_TEMPLATE_LOAD_TIME = true
[markup.sanitizer]
; The following keys can be used multiple times to define sanitation policy rules.
;ELEMENT = span
;ALLOW_ATTR = class
;REGEXP = ^(info|warning|error)$
[markup.asciidoc]
ENABLED = false
; List of file extensions that should be rendered by an external command
FILE_EXTENSIONS = .adoc,.asciidoc
; External command to render all matching extensions
RENDER_COMMAND = "asciidoc --out-file=- -"
; Don't pass the file on STDIN, pass the filename as argument instead.
IS_INPUT_FILE = false
[metrics]
; Enables metrics endpoint. True or false; default is false.
ENABLED = false
; If you want to add authorization, specify a token here
TOKEN =
[task]
; Task queue type, could be `channel` or `redis`.
QUEUE_TYPE = channel
; Task queue length, available only when `QUEUE_TYPE` is `channel`.
QUEUE_LENGTH = 1000
; Task queue connection string, available only when `QUEUE_TYPE` is `redis`.
; If there is a password of redis, use `addrs=127.0.0.1:6379 password=123 db=0`.
QUEUE_CONN_STR = "addrs=127.0.0.1:6379 db=0"
[migrations]
; Max attempts per http/https request on migrations.
MAX_ATTEMPTS = 3
; Backoff time per http/https request retry (seconds)
RETRY_BACKOFF = 3

14
docker/Makefile vendored
View File

@@ -1,14 +0,0 @@
#Makefile related to docker
DOCKER_IMAGE ?= gitea/gitea
DOCKER_TAG ?= latest
DOCKER_REF := $(DOCKER_IMAGE):$(DOCKER_TAG)
.PHONY: docker
docker:
docker build --disable-content-trust=false -t $(DOCKER_REF) .
# support also build args docker build --build-arg GITEA_VERSION=v1.2.3 --build-arg TAGS="bindata sqlite sqlite_unlock_notify" .
.PHONY: docker-build
docker-build:
docker run -ti --rm -v $(CURDIR):/srv/app/src/code.gitea.io/gitea -w /srv/app/src/code.gitea.io/gitea -e TAGS="bindata $(TAGS)" LDFLAGS="$(LDFLAGS)" webhippie/golang:edge make clean build

2
docker/etc/profile.d/gitea.sh Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
export GITEA_CUSTOM=/data/gitea

6
docker/etc/s6/gitea/run Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
[[ -f ./setup ]] && source ./setup
pushd /app/gitea > /dev/null
exec su-exec git /app/gitea/gitea web
popd

19
docker/etc/s6/gitea/setup Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/bash
if [ ! -d /data/git/.ssh ]; then
mkdir -p /data/git/.ssh
chmod 700 /data/git/.ssh
fi
if [ ! -f /data/git/.ssh/environment ]; then
echo "GITEA_CUSTOM=/data/gitea" >| /data/git/.ssh/environment
chmod 600 /data/git/.ssh/environment
fi
if [ ! -f /data/gitea/conf/app.ini ]; then
mkdir -p /data/gitea/conf
cp /etc/templates/app.ini /data/gitea/conf/app.ini
fi
chown -R git:git /data/gitea /app/gitea /data/git
chmod 0755 /data/gitea /app/gitea /data/git

2
docker/etc/s6/openssh/finish Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
exit 0

6
docker/etc/s6/openssh/run Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
[[ -f ./setup ]] && source ./setup
pushd /root > /dev/null
exec su-exec root /usr/sbin/sshd -D
popd

29
docker/etc/s6/openssh/setup Executable file
View File

@@ -0,0 +1,29 @@
#!/bin/bash
if [ ! -d /data/ssh ]; then
mkdir -p /data/ssh
fi
if [ ! -f /data/ssh/ssh_host_ed25519_key ]; then
echo "Generating /data/ssh/ssh_host_ed25519_key..."
ssh-keygen -t ed25519 -b 4096 -f /data/ssh/ssh_host_ed25519_key -N "" > /dev/null
fi
if [ ! -f /data/ssh/ssh_host_rsa_key ]; then
echo "Generating /data/ssh/ssh_host_rsa_key..."
ssh-keygen -t rsa -b 2048 -f /data/ssh/ssh_host_rsa_key -N "" > /dev/null
fi
if [ ! -f /data/ssh/ssh_host_dsa_key ]; then
echo "Generating /data/ssh/ssh_host_dsa_key..."
ssh-keygen -t dsa -f /data/ssh/ssh_host_dsa_key -N "" > /dev/null
fi
if [ ! -f /data/ssh/ssh_host_ecdsa_key ]; then
echo "Generating /data/ssh/ssh_host_ecdsa_key..."
ssh-keygen -t ecdsa -b 256 -f /data/ssh/ssh_host_ecdsa_key -N "" > /dev/null
fi
chown root:root /data/ssh/*
chmod 0700 /data/ssh
chmod 0600 /data/ssh/*

2
docker/etc/s6/syslogd/finish Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
exit 0

6
docker/etc/s6/syslogd/run Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
[[ -f ./setup ]] && source ./setup
pushd /root > /dev/null
exec su-exec root /sbin/syslogd -nS -O-
popd

1
docker/etc/s6/syslogd/setup Executable file
View File

@@ -0,0 +1 @@
#!/bin/bash

View File

@@ -1,4 +1,4 @@
Port ${SSH_LISTEN_PORT}
Port 22
Protocol 2
AddressFamily any
@@ -25,9 +25,8 @@ ChallengeResponseAuthentication no
PasswordAuthentication no
PermitEmptyPasswords no
AllowUsers ${USER}
AllowUsers git
Banner none
Subsystem sftp /usr/lib/ssh/sftp-server
AcceptEnv GIT_PROTOCOL
UsePrivilegeSeparation no

View File

@@ -0,0 +1,24 @@
[repository]
ROOT = /data/git/repositories
[repository.upload]
TEMP_PATH = /data/gitea/uploads
[server]
APP_DATA_PATH = /data/gitea
[database]
HOST = mysql:3306
PATH = /data/gitea/gitea.db
[session]
PROVIDER_CONFIG = /data/gitea/sessions
[picture]
AVATAR_UPLOAD_PATH = /data/gitea/avatars
[attachment]
PATH = /data/gitea/attachments
[log]
ROOT_PATH = /data/gitea/log

19
docker/manifest.tmpl vendored
View File

@@ -1,19 +0,0 @@
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
{{#if build.tags}}
tags:
{{#each build.tags}}
- {{this}}
{{/each}}
{{/if}}
manifests:
-
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
platform:
architecture: amd64
os: linux
-
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
platform:
architecture: arm64
os: linux
variant: v8

View File

@@ -1,2 +0,0 @@
#!/bin/bash
s6-svscanctl -t /etc/s6/

View File

@@ -1,6 +0,0 @@
#!/bin/bash
[[ -f ./setup ]] && source ./setup
pushd /app/gitea > /dev/null
exec su-exec $USER /app/gitea/gitea web
popd

View File

@@ -1,54 +0,0 @@
#!/bin/bash
if [ ! -d /data/git/.ssh ]; then
mkdir -p /data/git/.ssh
chmod 700 /data/git/.ssh
fi
if [ ! -f /data/git/.ssh/environment ]; then
echo "GITEA_CUSTOM=$GITEA_CUSTOM" >| /data/git/.ssh/environment
chmod 600 /data/git/.ssh/environment
elif ! grep -q "^GITEA_CUSTOM=$GITEA_CUSTOM$" /data/git/.ssh/environment; then
sed -i /^GITEA_CUSTOM=/d /data/git/.ssh/environment
echo "GITEA_CUSTOM=$GITEA_CUSTOM" >> /data/git/.ssh/environment
fi
if [ ! -f ${GITEA_CUSTOM}/conf/app.ini ]; then
mkdir -p ${GITEA_CUSTOM}/conf
# Set INSTALL_LOCK to true only if SECRET_KEY is not empty and
# INSTALL_LOCK is empty
if [ -n "$SECRET_KEY" ] && [ -z "$INSTALL_LOCK" ]; then
INSTALL_LOCK=true
fi
# Substitude the environment variables in the template
APP_NAME=${APP_NAME:-"Gitea: Git with a cup of tea"} \
RUN_MODE=${RUN_MODE:-"dev"} \
SSH_DOMAIN=${SSH_DOMAIN:-"localhost"} \
HTTP_PORT=${HTTP_PORT:-"3000"} \
ROOT_URL=${ROOT_URL:-""} \
DISABLE_SSH=${DISABLE_SSH:-"false"} \
SSH_PORT=${SSH_PORT:-"22"} \
SSH_LISTEN_PORT=${SSH_LISTEN_PORT:-"${SSH_PORT}"} \
LFS_START_SERVER=${LFS_START_SERVER:-"false"} \
DB_TYPE=${DB_TYPE:-"sqlite3"} \
DB_HOST=${DB_HOST:-"localhost:3306"} \
DB_NAME=${DB_NAME:-"gitea"} \
DB_USER=${DB_USER:-"root"} \
DB_PASSWD=${DB_PASSWD:-""} \
INSTALL_LOCK=${INSTALL_LOCK:-"false"} \
DISABLE_REGISTRATION=${DISABLE_REGISTRATION:-"false"} \
REQUIRE_SIGNIN_VIEW=${REQUIRE_SIGNIN_VIEW:-"false"} \
SECRET_KEY=${SECRET_KEY:-""} \
envsubst < /etc/templates/app.ini > ${GITEA_CUSTOM}/conf/app.ini
chown ${USER}:git ${GITEA_CUSTOM}/conf/app.ini
fi
# only chown if current owner is not already the gitea ${USER}. No recursive check to save time
if ! [[ $(ls -ld /data/gitea | awk '{print $3}') = ${USER} ]]; then chown -R ${USER}:git /data/gitea; fi
if ! [[ $(ls -ld /app/gitea | awk '{print $3}') = ${USER} ]]; then chown -R ${USER}:git /app/gitea; fi
if ! [[ $(ls -ld /data/git | awk '{print $3}') = ${USER} ]]; then chown -R ${USER}:git /data/git; fi
chmod 0755 /data/gitea /app/gitea /data/git

View File

@@ -1,6 +0,0 @@
#!/bin/bash
[[ -f ./setup ]] && source ./setup
pushd /root > /dev/null
exec su-exec root /usr/sbin/sshd -D -e 2>&1
popd

View File

@@ -1,37 +0,0 @@
#!/bin/bash
if [ ! -d /data/ssh ]; then
mkdir -p /data/ssh
fi
if [ ! -f /data/ssh/ssh_host_ed25519_key ]; then
echo "Generating /data/ssh/ssh_host_ed25519_key..."
ssh-keygen -t ed25519 -f /data/ssh/ssh_host_ed25519_key -N "" > /dev/null
fi
if [ ! -f /data/ssh/ssh_host_rsa_key ]; then
echo "Generating /data/ssh/ssh_host_rsa_key..."
ssh-keygen -t rsa -b 2048 -f /data/ssh/ssh_host_rsa_key -N "" > /dev/null
fi
if [ ! -f /data/ssh/ssh_host_dsa_key ]; then
echo "Generating /data/ssh/ssh_host_dsa_key..."
ssh-keygen -t dsa -f /data/ssh/ssh_host_dsa_key -N "" > /dev/null
fi
if [ ! -f /data/ssh/ssh_host_ecdsa_key ]; then
echo "Generating /data/ssh/ssh_host_ecdsa_key..."
ssh-keygen -t ecdsa -b 256 -f /data/ssh/ssh_host_ecdsa_key -N "" > /dev/null
fi
if [ -d /etc/ssh ]; then
SSH_PORT=${SSH_PORT:-"22"} \
SSH_LISTEN_PORT=${SSH_LISTEN_PORT:-"${SSH_PORT}"} \
envsubst < /etc/templates/sshd_config > /etc/ssh/sshd_config
chmod 0644 /etc/ssh/sshd_config
fi
chown root:root /data/ssh/*
chmod 0700 /data/ssh
chmod 0600 /data/ssh/*

View File

@@ -1,54 +0,0 @@
APP_NAME = $APP_NAME
RUN_MODE = $RUN_MODE
[repository]
ROOT = /data/git/repositories
[repository.local]
LOCAL_COPY_PATH = /data/gitea/tmp/local-repo
[repository.upload]
TEMP_PATH = /data/gitea/uploads
[server]
APP_DATA_PATH = /data/gitea
SSH_DOMAIN = $SSH_DOMAIN
HTTP_PORT = $HTTP_PORT
ROOT_URL = $ROOT_URL
DISABLE_SSH = $DISABLE_SSH
SSH_PORT = $SSH_PORT
SSH_LISTEN_PORT = $SSH_LISTEN_PORT
LFS_START_SERVER = $LFS_START_SERVER
LFS_CONTENT_PATH = /data/git/lfs
[database]
PATH = /data/gitea/gitea.db
DB_TYPE = $DB_TYPE
HOST = $DB_HOST
NAME = $DB_NAME
USER = $DB_USER
PASSWD = $DB_PASSWD
[indexer]
ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve
[session]
PROVIDER_CONFIG = /data/gitea/sessions
[picture]
AVATAR_UPLOAD_PATH = /data/gitea/avatars
REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars
[attachment]
PATH = /data/gitea/attachments
[log]
ROOT_PATH = /data/gitea/log
[security]
INSTALL_LOCK = $INSTALL_LOCK
SECRET_KEY = $SECRET_KEY
[service]
DISABLE_REGISTRATION = $DISABLE_REGISTRATION
REQUIRE_SIGNIN_VIEW = $REQUIRE_SIGNIN_VIEW

View File

@@ -1,35 +0,0 @@
#!/bin/sh
if [ "${USER}" != "git" ]; then
# rename user
sed -i -e "s/^git\:/${USER}\:/g" /etc/passwd
fi
if [ -z "${USER_GID}" ]; then
USER_GID="`id -g ${USER}`"
fi
if [ -z "${USER_UID}" ]; then
USER_UID="`id -u ${USER}`"
fi
## Change GID for USER?
if [ -n "${USER_GID}" ] && [ "${USER_GID}" != "`id -g ${USER}`" ]; then
sed -i -e "s/^${USER}:\([^:]*\):[0-9]*/${USER}:\1:${USER_GID}/" /etc/group
sed -i -e "s/^${USER}:\([^:]*\):\([0-9]*\):[0-9]*/${USER}:\1:\2:${USER_GID}/" /etc/passwd
fi
## Change UID for USER?
if [ -n "${USER_UID}" ] && [ "${USER_UID}" != "`id -u ${USER}`" ]; then
sed -i -e "s/^${USER}:\([^:]*\):[0-9]*:\([0-9]*\)/${USER}:\1:${USER_UID}:\2/" /etc/passwd
fi
for FOLDER in /data/gitea/conf /data/gitea/log /data/git /data/ssh; do
mkdir -p ${FOLDER}
done
if [ $# -gt 0 ]; then
exec "$@"
else
exec /bin/s6-svscan /etc/s6
fi

11
docker/usr/bin/entrypoint Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
for FOLDER in /data/gitea/conf /data/gitea/log /data/git /data/ssh; do
mkdir -p ${FOLDER}
done
if [ $# -gt 0 ]; then
exec "$@"
else
exec /bin/s6-svscan /etc/s6
fi

View File

@@ -1,34 +0,0 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
[*.go]
indent_style = tab
indent_size = 8
[*.{tmpl,html}]
indent_style = tab
indent_size = 4
[*.{less}]
indent_style = space
indent_size = 4
[*.{yml}]
indent_style = space
indent_size = 2
[*.js]
indent_style = space
indent_size = 4
[Makefile]
indent_style = tab
[*.md]
trim_trailing_whitespace = false

4
docs/.gitignore vendored
View File

@@ -1,4 +0,0 @@
public/
templates/swagger/v1_json.tmpl
themes/
resources/

View File

@@ -1,30 +0,0 @@
THEME := themes/gitea
PUBLIC := public
ARCHIVE := https://dl.gitea.io/theme/master.tar.gz
.PHONY: all
all: build
.PHONY: clean
clean:
rm -rf $(PUBLIC) $(THEME)
.PHONY: trans-copy
trans-copy:
@bash scripts/trans-copy
.PHONY: server
server: $(THEME)
hugo server
.PHONY: build
build: $(THEME)
hugo --cleanDestinationDir
.PHONY: update
update: $(THEME)
$(THEME): $(THEME)/theme.toml
$(THEME)/theme.toml:
mkdir -p $$(dirname $@)
curl -s $(ARCHIVE) | tar xz -C $$(dirname $@)

View File

@@ -1,51 +0,0 @@
# Gitea: Docs
[![Join the chat at https://img.shields.io/discord/322538954119184384.svg](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/Gitea)
[![](https://images.microbadger.com/badges/image/gitea/docs.svg)](http://microbadger.com/images/gitea/docs "Get your own image badge on microbadger.com")
## Hosting
These pages are hosted using [netlifycms](https://www.netlifycms.org/) and get
automatically updated on every push to the `master` branch.
## Install
These pages use the [Hugo](https://gohugo.io/) static site generator.
If you are planning to contribute you'll want to download and install Hugo on
your local machine.
The installation of Hugo is out of the scope of this document, so please take
the [official install instructions](https://gohugo.io/overview/installing/) to
get Hugo up and running.
## Development
To generate the website and serve it on [localhost:1313](http://localhost:1313)
just execute this command and stop it with `Ctrl+C`:
```
make server
```
When you are done with your changes just create a pull request, after merging
the pull request the website will be updated automatically.
## Contributing
Fork -> Patch -> Push -> Pull Request
## Authors
* [Maintainers](https://github.com/orgs/go-gitea/people)
* [Contributors](https://github.com/go-gitea/docs/graphs/contributors)
## License
This project is under the Apache-2.0 License. See the [LICENSE](LICENSE) file
for the full license text.
## Copyright
```
Copyright (c) 2016 The Gitea Authors <https://gitea.io>
```

View File

@@ -1,42 +0,0 @@
# Gitea: 文档
[![Build Status](http://drone.gitea.io/api/badges/go-gitea/docs/status.svg)](http://drone.gitea.io/go-gitea/docs)
[![Join the chat at https://img.shields.io/discord/322538954119184384.svg](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/NsatcWJ)
[![](https://images.microbadger.com/badges/image/gitea/docs.svg)](http://microbadger.com/images/gitea/docs "Get your own image badge on microbadger.com")
## 关于托管方式
本页面托管在我们 Docker 容器内的基础设施上, 它会在每次推送到 `master` 分支的时候自动更新,如果你想自己管理这个页面,你可以从我们的 Docker 镜像 [gitea/docs](https://hub.docker.com/r/gitea/docs/) 中获取它。
## 安装 Hugo
本页面使用了 [Hugo](https://github.com/spf13/hugo) 静态页面生成工具,如果您有维护它的意愿,则需要在本地计算机上下载并安装 Hugo。Hugo 的安装教程不在本文档的讲述范围之内,详情请参见 [官方文档](https://gohugo.io/overview/installing/)。
## 如何部署
在 [localhost:1313](http://localhost:1313) 处构建和运行网站的命令如下,如果需要停止可以使用组合键 `Ctrl+C`:
```
make server
```
完成更改后,只需创建一个 Pull Request (PR),该 PR 一经合并网站将自动更新。
## 如何贡献您的代码
Fork -> Patch -> Push -> Pull Request
## 关于我们
* [维护者信息](https://github.com/orgs/go-gitea/people)
* [代码贡献者信息](https://github.com/go-gitea/docs/graphs/contributors)
## 许可证
此项目采用 Apache-2.0 许可协议,请参见 [协议文件](LICENSE) 获取更多信息。
## 版权声明
```
Copyright (c) 2016 The Gitea Authors <https://gitea.io>
```

View File

@@ -1,176 +0,0 @@
function ready(fn) {
if (document.readyState != 'loading') {
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
ready(doSearch);
const summaryInclude = 60;
const fuseOptions = {
shouldSort: true,
includeMatches: true,
matchAllTokens: true,
threshold: 0.0, // for parsing diacritics
tokenize: true,
location: 0,
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: [{
name: "title",
weight: 0.8
},
{
name: "contents",
weight: 0.5
},
{
name: "tags",
weight: 0.3
},
{
name: "categories",
weight: 0.3
}
]
};
function param(name) {
return decodeURIComponent((location.search.split(name + '=')[1] || '').split('&')[0]).replace(/\+/g, ' ');
}
let searchQuery = param("s");
function doSearch() {
if (searchQuery) {
document.getElementById("search-query").value = searchQuery;
executeSearch(searchQuery);
} else {
const para = document.createElement("P");
para.innerText = "Please enter a word or phrase above";
document.getElementById("search-results").appendChild(para);
}
}
function getJSON(url, fn) {
const request = new XMLHttpRequest();
request.open('GET', url, true);
request.onload = function () {
if (request.status >= 200 && request.status < 400) {
const data = JSON.parse(request.responseText);
fn(data);
} else {
console.log("Target reached on " + url + " with error " + request.status);
}
};
request.onerror = function () {
console.log("Connection error " + request.status);
};
request.send();
}
function executeSearch(searchQuery) {
getJSON("/" + document.LANG + "/index.json", function (data) {
const pages = data;
const fuse = new Fuse(pages, fuseOptions);
const result = fuse.search(searchQuery);
console.log({
"matches": result
});
document.getElementById("search-results").innerHTML = "";
if (result.length > 0) {
populateResults(result);
} else {
const para = document.createElement("P");
para.innerText = "No matches found";
document.getElementById("search-results").appendChild(para);
}
});
}
function populateResults(result) {
result.forEach(function (value, key) {
const content = value.item.contents;
let snippet = "";
const snippetHighlights = [];
if (fuseOptions.tokenize) {
snippetHighlights.push(searchQuery);
value.matches.forEach(function (mvalue) {
if (mvalue.key === "tags" || mvalue.key === "categories") {
snippetHighlights.push(mvalue.value);
} else if (mvalue.key === "contents") {
const ind = content.toLowerCase().indexOf(searchQuery.toLowerCase());
const start = ind - summaryInclude > 0 ? ind - summaryInclude : 0;
const end = ind + searchQuery.length + summaryInclude < content.length ? ind + searchQuery.length + summaryInclude : content.length;
snippet += content.substring(start, end);
if (ind > -1) {
snippetHighlights.push(content.substring(ind, ind + searchQuery.length))
} else {
snippetHighlights.push(mvalue.value.substring(mvalue.indices[0][0], mvalue.indices[0][1] - mvalue.indices[0][0] + 1));
}
}
});
}
if (snippet.length < 1) {
snippet += content.substring(0, summaryInclude * 2);
}
//pull template from hugo templarte definition
const templateDefinition = document.getElementById("search-result-template").innerHTML;
//replace values
const output = render(templateDefinition, {
key: key,
title: value.item.title,
link: value.item.permalink,
tags: value.item.tags,
categories: value.item.categories,
snippet: snippet
});
document.getElementById("search-results").appendChild(htmlToElement(output));
snippetHighlights.forEach(function (snipvalue) {
new Mark(document.getElementById("summary-" + key)).mark(snipvalue);
});
});
}
function render(templateString, data) {
let conditionalMatches, copy;
const conditionalPattern = /\$\{\s*isset ([a-zA-Z]*) \s*\}(.*)\$\{\s*end\s*}/g;
//since loop below depends on re.lastInxdex, we use a copy to capture any manipulations whilst inside the loop
copy = templateString;
while ((conditionalMatches = conditionalPattern.exec(templateString)) !== null) {
if (data[conditionalMatches[1]]) {
//valid key, remove conditionals, leave content.
copy = copy.replace(conditionalMatches[0], conditionalMatches[2]);
} else {
//not valid, remove entire section
copy = copy.replace(conditionalMatches[0], '');
}
}
templateString = copy;
//now any conditionals removed we can do simple substitution
let key, find, re;
for (key in data) {
find = '\\$\\{\\s*' + key + '\\s*\\}';
re = new RegExp(find, 'g');
templateString = templateString.replace(re, data[key]);
}
return templateString;
}
/**
* By Mark Amery: https://stackoverflow.com/a/35385518
* @param {String} HTML representing a single element
* @return {Element}
*/
function htmlToElement(html) {
const template = document.createElement('template');
html = html.trim(); // Never return a text node of whitespace as the result
template.innerHTML = html;
return template.content.firstChild;
}

View File

@@ -1,287 +0,0 @@
baseurl: https://docs.gitea.io/
languageCode: en-us
title: Docs
theme: gitea
defaultContentLanguage: en-us
defaultContentLanguageInSubdir: true
enableMissingTranslationPlaceholders: true
enableEmoji: true
permalinks:
post: /:year/:month/:title/
doc: /:slug/
page: /:slug/
default: /:slug/
params:
description: Git with a cup of tea
author: The Gitea Authors
website: https://docs.gitea.io
version: 1.10.2
outputs:
home:
- HTML
- RSS
- JSON
menu:
page:
- name: Website
url: https://gitea.io/en-us/
weight: 10
pre: home
- name: Docs
url: /en-us/
weight: 20
pre: question
post: active
- name: API
url: https://try.gitea.io/api/swagger
weight: 45
pre: plug
- name: Blog
url: https://blog.gitea.io/
weight: 30
pre: rss
- name: Code
url: https://code.gitea.io/
weight: 40
pre: code
- name: Downloads
url: https://dl.gitea.io/
weight: 50
pre: download
- name: GitHub
url: https://github.com/go-gitea/
weight: 60
pre: github
- name: Discord Chat
url: https://discord.gg/NsatcWJ
weight: 70
pre: comment
- name: Forum
url: https://discourse.gitea.io/
weight: 80
pre: group
languages:
en-us:
weight: 0
languageName: English
zh-cn:
weight: 1
languageName: 中文(简体)
menu:
page:
- name: 网站
url: https://gitea.io/zh-cn/
weight: 10
pre: home
- name: 文档
url: /zh-cn/
weight: 20
pre: question
post: active
- name: API
url: https://try.gitea.io/api/swagger
weight: 45
pre: plug
- name: 博客
url: https://blog.gitea.io/
weight: 30
pre: rss
- name: 导入
url: https://code.gitea.io/
weight: 40
pre: code
- name: 下载
url: https://dl.gitea.io/
weight: 50
pre: download
- name: GitHub
url: https://github.com/go-gitea/
weight: 60
pre: github
- name: Discord Chat
url: https://discord.gg/NsatcWJ
weight: 70
pre: comment
- name: Forum
url: https://discourse.gitea.io/
weight: 80
pre: group
zh-tw:
weight: 2
languageName: 中文(繁體)
menu:
page:
- name: 網站
url: https://gitea.io/zh-tw/
weight: 10
pre: home
- name: 文件
url: /zh-tw/
weight: 20
pre: question
post: active
- name: API
url: https://try.gitea.io/api/swagger
weight: 45
pre: plug
- name: 部落格
url: https://blog.gitea.io/
weight: 30
pre: rss
- name: 程式碼
url: https://code.gitea.io/
weight: 40
pre: code
- name: 下载
url: https://dl.gitea.io/
weight: 50
pre: download
- name: GitHub
url: https://github.com/go-gitea/
weight: 60
pre: github
- name: Discord Chat
url: https://discord.gg/NsatcWJ
weight: 70
pre: comment
- name: Forum
url: https://discourse.gitea.io/
weight: 80
pre: group
pt-br:
weight: 3
languageName: Português Brasileiro
menu:
page:
- name: Página inicial
url: https://gitea.io/pt-br/
weight: 10
pre: home
- name: Documentação
url: /pt-br/
weight: 20
pre: question
post: active
- name: API
url: https://try.gitea.io/api/swagger
weight: 45
pre: plug
- name: Blog
url: https://blog.gitea.io/
weight: 30
pre: rss
- name: Código-fonte
url: https://code.gitea.io/
weight: 40
pre: code
- name: Downloads
url: https://dl.gitea.io/
weight: 50
pre: download
- name: GitHub
url: https://github.com/go-gitea/
weight: 60
pre: github
- name: Chat no Discord
url: https://discord.gg/NsatcWJ
weight: 70
pre: comment
- name: Forum
url: https://discourse.gitea.io/
weight: 80
pre: group
nl-nl:
weight: 4
languageName: Nederlands
menu:
page:
- name: Website
url: https://gitea.io/nl-nl/
weight: 10
pre: home
- name: Docs
url: /nl-nl/
weight: 20
pre: question
post: active
- name: API
url: https://try.gitea.io/api/swagger
weight: 45
pre: plug
- name: Blog
url: https://blog.gitea.io/
weight: 30
pre: rss
- name: Code
url: https://code.gitea.io/
weight: 40
pre: code
- name: Downloads
url: https://dl.gitea.io/
weight: 50
pre: download
- name: GitHub
url: https://github.com/go-gitea/
weight: 60
pre: github
- name: Discord Chat
url: https://discord.gg/NsatcWJ
weight: 70
pre: comment
- name: Forum
url: https://discourse.gitea.io/
weight: 80
pre: group
fr-fr:
weight: 5
languageName: Français
menu:
page:
- name: Site
url: https://gitea.io/en-us/
weight: 10
pre: home
post: active
- name: Documentation
url: /fr-fr/
weight: 20
pre: question
- name: API
url: https://try.gitea.io/api/swagger
weight: 45
pre: plug
- name: Blog
url: https://blog.gitea.io/
weight: 30
pre: rss
- name: Code
url: https://code.gitea.io/
weight: 40
pre: code
- name: Téléchargement
url: https://dl.gitea.io/
weight: 50
pre: download
- name: GitHub
url: https://github.com/go-gitea/
weight: 60
pre: github
- name: Discord Chat
url: https://discord.gg/NsatcWJ
weight: 70
pre: comment
- name: Forum
url: https://discourse.gitea.io/
weight: 80
pre: group

View File

@@ -1,13 +0,0 @@
---
date: "2016-12-01T16:00:00+02:00"
title: "Advanced"
slug: "advanced"
weight: 30
toc: false
draft: false
menu:
sidebar:
name: "Advanced"
weight: 40
identifier: "advanced"
---

View File

@@ -1,13 +0,0 @@
---
date: "2017-08-23T09:00:00+02:00"
title: "Avancé"
slug: "advanced"
weight: 30
toc: false
draft: false
menu:
sidebar:
name: "Avancé"
weight: 40
identifier: "advanced"
---

View File

@@ -1,13 +0,0 @@
---
date: "2016-12-01T16:00:00+02:00"
title: "进阶"
slug: "advanced"
weight: 30
toc: false
draft: false
menu:
sidebar:
name: "进阶"
weight: 40
identifier: "advanced"
---

View File

@@ -1,101 +0,0 @@
---
date: "2018-06-24:00:00+02:00"
title: "API Usage"
slug: "api-usage"
weight: 40
toc: true
draft: false
menu:
sidebar:
parent: "advanced"
name: "API Usage"
weight: 40
identifier: "api-usage"
---
# Gitea API Usage
## Enabling/configuring API access
By default, `ENABLE_SWAGGER` is true, and
`MAX_RESPONSE_ITEMS` is set to 50. See [Config Cheat
Sheet](https://docs.gitea.io/en-us/config-cheat-sheet/) for more
information.
## Authentication via the API
Gitea supports these methods of API authentication:
- HTTP basic authentication
- `token=...` parameter in URL query string
- `access_token=...` parameter in URL query string
- `Authorization: token ...` header in HTTP headers
All of these methods accept the same API key token type. You can
better understand this by looking at the code -- as of this writing,
Gitea parses queries and headers to find the token in
[modules/auth/auth.go](https://github.com/go-gitea/gitea/blob/6efdcaed86565c91a3dc77631372a9cc45a58e89/modules/auth/auth.go#L47).
You can create an API key token via your Gitea installation's web interface:
`Settings | Applications | Generate New Token`.
### OAuth2
Access tokens obtained from Gitea's [OAuth2 provider](https://docs.gitea.io/en-us/oauth2-provider) are accepted by these methods:
- `Authorization bearer ...` header in HTTP headers
- `token=...` parameter in URL query string
- `access_token=...` parameter in URL query string
### More on the `Authorization:` header
For historical reasons, Gitea needs the word `token` included before
the API key token in an authorization header, like this:
```
Authorization: token 65eaa9c8ef52460d22a93307fe0aee76289dc675
```
In a `curl` command, for instance, this would look like:
```
curl -X POST "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
```
As mentioned above, the token used is the same one you would use in
the `token=` string in a GET request.
## API Guide:
API Reference guide is auto-generated by swagger and available on:
`https://gitea.your.host/api/swagger`
or on
[gitea demo instance](https://try.gitea.io/api/swagger)
## Listing your issued tokens via the API
As mentioned in
[#3842](https://github.com/go-gitea/gitea/issues/3842#issuecomment-397743346),
`/users/:name/tokens` is special and requires you to authenticate
using BasicAuth, as follows:
### Using basic authentication:
```
$ curl --request GET --url https://yourusername:yourpassword@gitea.your.host/api/v1/users/yourusername/tokens
[{"name":"test","sha1":"..."},{"name":"dev","sha1":"..."}]
```
As of v1.8.0 of Gitea, if using basic authentication with the API and your user has two factor authentication enabled, you'll need to send an additional header that contains the one time password (6 digit rotating token). An example of the header is `X-Gitea-OTP: 123456` where `123456` is where you'd place the code from your authenticator. Here is how the request would look like in curl:
```
$ curl -H "X-Gitea-OTP: 123456" --request GET --url https://yourusername:yourpassword@gitea.your.host/api/v1/users/yourusername/tokens
```
## Sudo
The API allows admin users to sudo API requests as another user. Simply add either a `sudo=` parameter or `Sudo:` request header with the username of the user to sudo.

View File

@@ -1,71 +0,0 @@
---
date: "2018-06-24:00:00+02:00"
title: "API 使用指南"
slug: "api-usage"
weight: 40
toc: true
draft: false
menu:
sidebar:
parent: "advanced"
name: "API 使用指南"
weight: 40
identifier: "api-usage"
---
# Gitea API 使用指南
## 开启/配置 API 访问
通常情况下, `ENABLE_SWAGGER` 默认开启并且参数 `MAX_RESPONSE_ITEMS` 默认为 50。您可以从 [Config Cheat
Sheet](https://docs.gitea.io/en-us/config-cheat-sheet/) 中获取更多配置相关信息。
## 通过 API 认证
Gitea 支持以下几种 API 认证方式:
- HTTP basic authentication 方式
- 通过指定 `token=...` URL 查询参数方式
- 通过指定 `access_token=...` URL 查询参数方式
- 通过指定 `Authorization: token ...` HTTP header 方式
以上提及的认证方法接受相同的 apiKey token 类型,您可以在编码时通过查阅代码更好地理解这一点。
Gitea 调用解析查询参数以及头部信息来获取 token 的代码可以在 [modules/auth/auth.go](https://github.com/go-gitea/gitea/blob/6efdcaed86565c91a3dc77631372a9cc45a58e89/modules/auth/auth.go#L47) 中找到。
您可以通过您的 gitea web 界面来创建 apiKey token
`Settings | Applications | Generate New Token`.
### 关于 `Authorization:` header
由于一些历史原因Gitea 需要在 header 的 apiKey token 里引入前缀 `token`,类似于如下形式:
```
Authorization: token 65eaa9c8ef52460d22a93307fe0aee76289dc675
```
`curl` 命令为例,它会以如下形式携带在请求中:
```
curl -X POST "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
```
正如上例所示,您也可以在 GET 请求中使用同一个 token 并以 `token=` 的查询参数形式携带 token 来进行认证。
## 通过 API 列出您发布的令牌
`/users/:name/tokens` 是一个特殊的接口,需要您使用 basic authentication 进行认证,具体原因在 issue 中
[#3842](https://github.com/go-gitea/gitea/issues/3842#issuecomment-397743346) 有所提及,使用方法如下所示:
### 使用 Basic authentication 认证:
```
$ curl --request GET --url https://yourusername:yourpassword@gitea.your.host/api/v1/users/yourusername/tokens
[{"name":"test","sha1":"..."},{"name":"dev","sha1":"..."}]
```
## 使用 Sudo 方式请求 API
此 API 允许管理员借用其他用户身份进行 API 请求。只需在请求中指定查询参数 `sudo=` 或是指定 header 中的 `Sudo:` 为需要使用的用户 username 即可。

View File

@@ -1,34 +0,0 @@
---
date: "2019-08-27:00:00+02:00"
title: "CI/CD Usage"
slug: "ci-cd"
weight: 40
toc: true
draft: false
menu:
sidebar:
parent: "advanced"
name: "CI/CD Usage"
weight: 40
identifier: "ci-cd"
---
# Gitea and CI/CD
**NOTE:** These tools are not endorsed by Gitea. They are listed here for convenience only.
## Listing
CI/CD solutions that have integration with Gitea. Following list is not complete,
the purpose is to give a starting point to integrate a CI/CD process with your Gitea instance.
- [Drone](https://drone.io) with [Gitea documentation](https://docs.drone.io/installation/providers/gitea/)
- [Jenkins](https://jenkins.io/) with [Gitea plugin](https://plugins.jenkins.io/gitea)
- [Agola](https://agola.io)
- [Buildkite](https://buildkite.com) with [Gitea connector](https://github.com/techknowlogick/gitea-buildkite-connector)
- [AppVeyor](https://www.appveyor.com) with [built-in Gitea support](https://www.appveyor.com/blog/2019/09/05/gitea-receives-first-class-support-in-appveyor/)
- [Buildbot](https://www.buildbot.net/) with [Gitea plugin](https://github.com/lab132/buildbot-gitea)
Others CI/CD solutions that partially can be integrated with Gitea:
- [Concourse](https://www.concourse-ci.org), see more information at [Concourse community forum](https://discuss.concourse-ci.org/t/concourse-ci-and-gitea-oauth/1475)

View File

@@ -1,650 +0,0 @@
---
date: "2016-12-26T16:00:00+02:00"
title: "Config Cheat Sheet"
slug: "config-cheat-sheet"
weight: 20
toc: false
draft: false
menu:
sidebar:
parent: "advanced"
name: "Config Cheat Sheet"
weight: 20
identifier: "config-cheat-sheet"
---
# Configuration Cheat Sheet
This is a cheat sheet for the Gitea configuration file. It contains most of the settings
that can be configured as well as their default values.
Any changes to the Gitea configuration file should be made in `custom/conf/app.ini`
or any corresponding location. When installing from a distribution, this will
typically be found at `/etc/gitea/conf/app.ini`.
The defaults provided here are best-effort (not built automatically). They are
accurately recorded in [app.ini.sample](https://github.com/go-gitea/gitea/blob/master/custom/conf/app.ini.sample)
(s/master/\<tag|release\>). Any string in the format `%(X)s` is a feature powered
by [ini](https://github.com/go-ini/ini/#recursive-values), for reading values recursively.
Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
**Note:** A full restart is required for Gitea configuration changes to take effect.
## Overall (`DEFAULT`)
- `APP_NAME`: **Gitea: Git with a cup of tea**: Application name, used in the page title.
- `RUN_USER`: **git**: The user Gitea will run as. This should be a dedicated system
(non-user) account. Setting this incorrectly will cause Gitea to not start.
- `RUN_MODE`: **dev**: For performance and other purposes, change this to `prod` when
deployed to a production environment. The installation process will set this to `prod`
automatically. \[prod, dev, test\]
## Repository (`repository`)
- `ROOT`: **~/gitea-repositories/**: Root path for storing all repository data. It must be
an absolute path.
- `SCRIPT_TYPE`: **bash**: The script type this server supports. Usually this is `bash`,
but some users report that only `sh` is available.
- `ANSI_CHARSET`: **\<empty\>**: The default charset for an unrecognized charset.
- `FORCE_PRIVATE`: **false**: Force every new repository to be private.
- `DEFAULT_PRIVATE`: **last**: Default private when creating a new repository.
\[last, private, public\]
- `MAX_CREATION_LIMIT`: **-1**: Global maximum creation limit of repositories per user,
`-1` means no limit.
- `PULL_REQUEST_QUEUE_LENGTH`: **1000**: Length of pull request patch test queue, make it
as large as possible. Use caution when editing this value.
- `MIRROR_QUEUE_LENGTH`: **1000**: Patch test queue length, increase if pull request patch
testing starts hanging.
- `PREFERRED_LICENSES`: **Apache License 2.0,MIT License**: Preferred Licenses to place at
the top of the list. Name must match file name in conf/license or custom/conf/license.
- `DISABLE_HTTP_GIT`: **false**: Disable the ability to interact with repositories over the
HTTP protocol.
- `USE_COMPAT_SSH_URI`: **false**: Force ssh:// clone url instead of scp-style uri when
default SSH port is used.
- `ACCESS_CONTROL_ALLOW_ORIGIN`: **\<empty\>**: Value for Access-Control-Allow-Origin header,
default is not to present. **WARNING**: This maybe harmful to you website if you do not
give it a right value.
- `DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH`: **false**: Close an issue if a commit on a non default branch marks it as closed.
- `ENABLE_PUSH_CREATE_USER`: **false**: Allow users to push local repositories to Gitea and have them automatically created for a user.
- `ENABLE_PUSH_CREATE_ORG`: **false**: Allow users to push local repositories to Gitea and have them automatically created for an org.
### Repository - Pull Request (`repository.pull-request`)
- `WORK_IN_PROGRESS_PREFIXES`: **WIP:,\[WIP\]**: List of prefixes used in Pull Request
title to mark them as Work In Progress
- `CLOSE_KEYWORDS`: **close**, **closes**, **closed**, **fix**, **fixes**, **fixed**, **resolve**, **resolves**, **resolved**: List of
keywords used in Pull Request comments to automatically close a related issue
- `REOPEN_KEYWORDS`: **reopen**, **reopens**, **reopened**: List of keywords used in Pull Request comments to automatically reopen
a related issue
- `DEFAULT_MERGE_MESSAGE_COMMITS_LIMIT`: **50**: In the default merge message for squash commits include at most this many commits. Set to `-1` to include all commits
- `DEFAULT_MERGE_MESSAGE_SIZE`: **5120**: In the default merge message for squash commits limit the size of the commit messages. Set to `-1` to have no limit.
- `DEFAULT_MERGE_MESSAGE_ALL_AUTHORS`: **false**: In the default merge message for squash commits walk all commits to include all authors in the Co-authored-by otherwise just use those in the limited list
- `DEFAULT_MERGE_MESSAGE_MAX_APPROVERS`: **10**: In default merge messages limit the number of approvers listed as `Reviewed-by:`. Set to `-1` to include all.
- `DEFAULT_MERGE_MESSAGE_OFFICIAL_APPROVERS_ONLY`: **true**: In default merge messages only include approvers who are officially allowed to review.
### Repository - Issue (`repository.issue`)
- `LOCK_REASONS`: **Too heated,Off-topic,Resolved,Spam**: A list of reasons why a Pull Request or Issue can be locked
### Repository - Signing (`repository.signing`)
- `SIGNING_KEY`: **default**: \[none, KEYID, default \]: Key to sign with.
- `SIGNING_NAME` &amp; `SIGNING_EMAIL`: if a KEYID is provided as the `SIGNING_KEY`, use these as the Name and Email address of the signer. These should match publicized name and email address for the key.
- `INITIAL_COMMIT`: **always**: \[never, pubkey, twofa, always\]: Sign initial commit.
- `never`: Never sign
- `pubkey`: Only sign if the user has a public key
- `twofa`: Only sign if the user is logged in with twofa
- `always`: Always sign
- Options other than `never` and `always` can be combined as a comma separated list.
- `WIKI`: **never**: \[never, pubkey, twofa, always, parentsigned\]: Sign commits to wiki.
- `CRUD_ACTIONS`: **pubkey, twofa, parentsigned**: \[never, pubkey, twofa, parentsigned, always\]: Sign CRUD actions.
- Options as above, with the addition of:
- `parentsigned`: Only sign if the parent commit is signed.
- `MERGES`: **pubkey, twofa, basesigned, commitssigned**: \[never, pubkey, twofa, approved, basesigned, commitssigned, always\]: Sign merges.
- `approved`: Only sign approved merges to a protected branch.
- `basesigned`: Only sign if the parent commit in the base repo is signed.
- `headsigned`: Only sign if the head commit in the head branch is signed.
- `commitssigned`: Only sign if all the commits in the head branch to the merge point are signed.
## CORS (`cors`)
- `ENABLED`: **false**: enable cors headers (disabled by default)
- `SCHEME`: **http**: scheme of allowed requests
- `ALLOW_DOMAIN`: **\***: list of requesting domains that are allowed
- `ALLOW_SUBDOMAIN`: **false**: allow subdomains of headers listed above to request
- `METHODS`: **GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS**: list of methods allowed to request
- `MAX_AGE`: **10m**: max time to cache response
- `ALLOW_CREDENTIALS`: **false**: allow request with credentials
## UI (`ui`)
- `EXPLORE_PAGING_NUM`: **20**: Number of repositories that are shown in one explore page.
- `ISSUE_PAGING_NUM`: **10**: Number of issues that are shown in one page (for all pages that list issues).
- `MEMBERS_PAGING_NUM`: **20**: Number of members that are shown in organization members.
- `FEED_MAX_COMMIT_NUM`: **5**: Number of maximum commits shown in one activity feed.
- `GRAPH_MAX_COMMIT_NUM`: **100**: Number of maximum commits shown in the commit graph.
- `DEFAULT_THEME`: **gitea**: \[gitea, arc-green\]: Set the default theme for the Gitea install.
- `THEMES`: **gitea,arc-green**: All available themes. Allow users select personalized themes
regardless of the value of `DEFAULT_THEME`.
- `REACTIONS`: All available reactions. Allow users react with different emoji's.
- `DEFAULT_SHOW_FULL_NAME`: **false**: Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used.
- `SEARCH_REPO_DESCRIPTION`: **true**: Whether to search within description at repository search on explore page.
- `USE_SERVICE_WORKER`: **true**: Whether to enable a Service Worker to cache frontend assets.
### UI - Admin (`ui.admin`)
- `USER_PAGING_NUM`: **50**: Number of users that are shown in one page.
- `REPO_PAGING_NUM`: **50**: Number of repos that are shown in one page.
- `NOTICE_PAGING_NUM`: **25**: Number of notices that are shown in one page.
- `ORG_PAGING_NUM`: **50**: Number of organizations that are shown in one page.
## Markdown (`markdown`)
- `ENABLE_HARD_LINE_BREAK`: **false**: Enable Markdown's hard line break extension.
- `CUSTOM_URL_SCHEMES`: Use a comma separated list (ftp,git,svn) to indicate additional
URL hyperlinks to be rendered in Markdown. URLs beginning in http and https are
always displayed
## Server (`server`)
- `PROTOCOL`: **http**: \[http, https, fcgi, unix, fcgi+unix\]
- `DOMAIN`: **localhost**: Domain name of this server.
- `ROOT_URL`: **%(PROTOCOL)s://%(DOMAIN)s:%(HTTP\_PORT)s/**:
Overwrite the automatically generated public URL.
This is useful if the internal and the external URL don't match (e.g. in Docker).
- `STATIC_URL_PREFIX`: **\<empty\>**:
Overwrite this option to request static resources from a different URL.
This includes CSS files, images, JS files and web fonts.
Avatar images are dynamic resources and still served by gitea.
The option can be just a different path, as in `/static`, or another domain, as in `https://cdn.example.com`.
Requests are then made as `%(ROOT_URL)s/static/css/index.css` and `https://cdn.example.com/css/index.css` respective.
The static files are located in the `public/` directory of the gitea source repository.
- `HTTP_ADDR`: **0.0.0.0**: HTTP listen address.
- If `PROTOCOL` is set to `fcgi`, Gitea will listen for FastCGI requests on TCP socket
defined by `HTTP_ADDR` and `HTTP_PORT` configuration settings.
- If `PROTOCOL` is set to `unix` or `fcgi+unix`, this should be the name of the Unix socket file to use.
- `HTTP_PORT`: **3000**: HTTP listen port.
- If `PROTOCOL` is set to `fcgi`, Gitea will listen for FastCGI requests on TCP socket
defined by `HTTP_ADDR` and `HTTP_PORT` configuration settings.
- `UNIX_SOCKET_PERMISSION`: **666**: Permissions for the Unix socket.
- `LOCAL_ROOT_URL`: **%(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/**: Local
(DMZ) URL for Gitea workers (such as SSH update) accessing web service. In
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 `unix`.
- `DISABLE_SSH`: **false**: Disable SSH feature when it's not available.
- `START_SSH_SERVER`: **false**: When enabled, use the built-in SSH server.
- `SSH_DOMAIN`: **%(DOMAIN)s**: Domain name of this server, used for displayed clone URL.
- `SSH_PORT`: **22**: SSH port displayed in clone URL.
- `SSH_LISTEN_HOST`: **0.0.0.0**: Listen address for the built-in SSH server.
- `SSH_LISTEN_PORT`: **%(SSH\_PORT)s**: Port for the built-in SSH server.
- `OFFLINE_MODE`: **false**: Disables use of CDN for static files and Gravatar for profile pictures.
- `DISABLE_ROUTER_LOG`: **false**: Mute printing of the router log.
- `CERT_FILE`: **custom/https/cert.pem**: Cert file path used for HTTPS.
- `KEY_FILE`: **custom/https/key.pem**: Key file path used for HTTPS.
- `STATIC_ROOT_PATH`: **./**: Upper level of template and static files path.
- `STATIC_CACHE_TIME`: **6h**: Web browser cache time for static resources on `custom/`, `public/` and all uploaded avatars.
- `ENABLE_GZIP`: **false**: Enables application-level GZIP support.
- `LANDING_PAGE`: **home**: Landing page for unauthenticated users \[home, explore, organizations, login\].
- `LFS_START_SERVER`: **false**: Enables git-lfs support.
- `LFS_CONTENT_PATH`: **./data/lfs**: Where to store LFS files.
- `LFS_JWT_SECRET`: **\<empty\>**: LFS authentication secret, change this a unique string.
- `LFS_HTTP_AUTH_EXPIRY`: **20m**: LFS authentication validity period in time.Duration, pushes taking longer than this may fail.
- `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.
- `PORT_TO_REDIRECT`: **80**: Port for the http redirection service to listen on. Used when `REDIRECT_OTHER_PORT` is true.
- `ENABLE_LETSENCRYPT`: **false**: If enabled you must set `DOMAIN` to valid internet facing domain (ensure DNS is set and port 80 is accessible by letsencrypt validation server).
By using Lets Encrypt **you must consent** to their [terms of service](https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf).
- `LETSENCRYPT_ACCEPTTOS`: **false**: This is an explicit check that you accept the terms of service for Let's Encrypt.
- `LETSENCRYPT_DIRECTORY`: **https**: Directory that Letsencrypt will use to cache information such as certs and private keys.
- `LETSENCRYPT_EMAIL`: **email@example.com**: Email used by Letsencrypt to notify about problems with issued certificates. (No default)
- `ALLOW_GRACEFUL_RESTARTS`: **true**: Perform a graceful restart on SIGHUP
- `GRACEFUL_HAMMER_TIME`: **60s**: After a restart the parent process will stop accepting new connections and will allow requests to finish before stopping. Shutdown will be forced if it takes longer than this time.
- `STARTUP_TIMEOUT`: **0**: Shutsdown the server if startup takes longer than the provided time. On Windows setting this sends a waithint to the SVC host to tell the SVC host startup may take some time. Please note startup is determined by the opening of the listeners - HTTP/HTTPS/SSH. Indexers may take longer to startup and can have their own timeouts.
## Database (`database`)
- `DB_TYPE`: **mysql**: The database type in use \[mysql, postgres, mssql, sqlite3\].
- `HOST`: **127.0.0.1:3306**: Database host address and port or absolute path for unix socket \[mysql, postgres\] (ex: /var/run/mysqld/mysqld.sock).
- `NAME`: **gitea**: Database name.
- `USER`: **root**: Database username.
- `PASSWD`: **\<empty\>**: Database user password. Use \`your password\` for quoting if you use special characters in the password.
- `SSL_MODE`: **disable**: For PostgreSQL and MySQL only.
- `CHARSET`: **utf8**: For MySQL only, either "utf8" or "utf8mb4", default is "utf8". NOTICE: for "utf8mb4" you must use MySQL InnoDB > 5.6. Gitea is unable to check this.
- `PATH`: **data/gitea.db**: For SQLite3 only, the database file path.
- `LOG_SQL`: **true**: Log the executed SQL.
- `DB_RETRIES`: **10**: How many ORM init / DB connect attempts allowed.
- `DB_RETRY_BACKOFF`: **3s**: time.Duration to wait before trying another ORM init / DB connect attempt, if failure occured.
- `MAX_OPEN_CONNS` **0**: Database maximum open connections - default is 0, meaning there is no limit.
- `MAX_IDLE_CONNS` **2**: Max idle database connections on connnection pool, default is 2 - this will be capped to `MAX_OPEN_CONNS`.
- `CONN_MAX_LIFETIME` **0 or 3s**: Sets the maximum amount of time a DB connection may be reused - default is 0, meaning there is no limit (except on MySQL where it is 3s - see #6804 & #7071).
Please see #8540 & #8273 for further discussion of the appropriate values for `MAX_OPEN_CONNS`, `MAX_IDLE_CONNS` & `CONN_MAX_LIFETIME` and their
relation to port exhaustion.
## Indexer (`indexer`)
- `ISSUE_INDEXER_TYPE`: **bleve**: Issue indexer type, currently support: bleve or db, if it's db, below issue indexer item will be invalid.
- `ISSUE_INDEXER_PATH`: **indexers/issues.bleve**: Index file used for issue search.
- The next 4 configuration values are deprecated and should be set in `queue.issue_indexer` however are kept for backwards compatibility:
- `ISSUE_INDEXER_QUEUE_TYPE`: **levelqueue**: Issue indexer queue, currently supports:`channel`, `levelqueue`, `redis`.
- `ISSUE_INDEXER_QUEUE_DIR`: **indexers/issues.queue**: When `ISSUE_INDEXER_QUEUE_TYPE` is `levelqueue`, this will be the queue will be saved path.
- `ISSUE_INDEXER_QUEUE_CONN_STR`: **addrs=127.0.0.1:6379 db=0**: When `ISSUE_INDEXER_QUEUE_TYPE` is `redis`, this will store the redis connection string.
- `ISSUE_INDEXER_QUEUE_BATCH_NUMBER`: **20**: Batch queue number.
- `REPO_INDEXER_ENABLED`: **false**: Enables code search (uses a lot of disk space, about 6 times more than the repository size).
- `REPO_INDEXER_PATH`: **indexers/repos.bleve**: Index file used for code search.
- `REPO_INDEXER_INCLUDE`: **empty**: A comma separated list of glob patterns (see https://github.com/gobwas/glob) to **include** in the index. Use `**.txt` to match any files with .txt extension. An empty list means include all files.
- `REPO_INDEXER_EXCLUDE`: **empty**: A comma separated list of glob patterns (see https://github.com/gobwas/glob) to **exclude** from the index. Files that match this list will not be indexed, even if they match in `REPO_INDEXER_INCLUDE`.
- `UPDATE_BUFFER_LEN`: **20**: Buffer length of index request.
- `MAX_FILE_SIZE`: **1048576**: Maximum size in bytes of files to be indexed.
- `STARTUP_TIMEOUT`: **30s**: If the indexer takes longer than this timeout to start - fail. (This timeout will be added to the hammer time above for child processes - as bleve will not start until the previous parent is shutdown.) Set to zero to never timeout.
## Queue (`queue` and `queue.*`)
- `TYPE`: **persistable-channel**: General queue type, currently support: `persistable-channel`, `channel`, `level`, `redis`, `dummy`
- `DATADIR`: **queues/**: Base DataDir for storing persistent and level queues. `DATADIR` for inidividual queues can be set in `queue.name` sections but will default to `DATADIR/`**`name`**.
- `LENGTH`: **20**: Maximal queue size before channel queues block
- `BATCH_LENGTH`: **20**: Batch data before passing to the handler
- `CONN_STR`: **addrs=127.0.0.1:6379 db=0**: Connection string for the redis queue type.
- `QUEUE_NAME`: **_queue**: The suffix for default redis queue name. Individual queues will default to **`name`**`QUEUE_NAME` but can be overriden in the specific `queue.name` section.
- `WRAP_IF_NECESSARY`: **true**: Will wrap queues with a timeoutable queue if the selected queue is not ready to be created - (Only relevant for the level queue.)
- `MAX_ATTEMPTS`: **10**: Maximum number of attempts to create the wrapped queue
- `TIMEOUT`: **GRACEFUL_HAMMER_TIME + 30s**: Timeout the creation of the wrapped queue if it takes longer than this to create.
- Queues by default come with a dynamically scaling worker pool. The following settings configure this:
- `WORKERS`: **1**: Number of initial workers for the queue.
- `MAX_WORKERS`: **10**: Maximum number of worker go-routines for the queue.
- `BLOCK_TIMEOUT`: **1s**: If the queue blocks for this time, boost the number of workers - the `BLOCK_TIMEOUT` will then be doubled before boosting again whilst the boost is ongoing.
- `BOOST_TIMEOUT`: **5m**: Boost workers will timeout after this long.
- `BOOST_WORKERS`: **5**: This many workers will be added to the worker pool if there is a boost.
## Admin (`admin`)
- `DEFAULT_EMAIL_NOTIFICATIONS`: **enabled**: Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled
## Security (`security`)
- `INSTALL_LOCK`: **false**: Disallow access to the install page.
- `SECRET_KEY`: **\<random at every install\>**: Global secret key. This should be changed.
- `LOGIN_REMEMBER_DAYS`: **7**: Cookie lifetime, in days.
- `COOKIE_USERNAME`: **gitea\_awesome**: Name of the cookie used to store the current username.
- `COOKIE_REMEMBER_NAME`: **gitea\_incredible**: Name of cookie used to store authentication
information.
- `REVERSE_PROXY_AUTHENTICATION_USER`: **X-WEBAUTH-USER**: Header name for reverse proxy
authentication.
- `REVERSE_PROXY_AUTHENTICATION_EMAIL`: **X-WEBAUTH-EMAIL**: Header name for reverse proxy
authentication provided email.
- `DISABLE_GIT_HOOKS`: **false**: Set to `true` to prevent all users (including admin) from creating custom
git hooks.
- `ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET`: **true**: Set to `false` to allow local users to push to gitea-repositories without setting up the Gitea environment. This is not recommended and if you want local users to push to gitea repositories you should set the environment appropriately.
- `IMPORT_LOCAL_PATHS`: **false**: Set to `false` to prevent all users (including admin) from importing local path on server.
- `INTERNAL_TOKEN`: **\<random at every install if no uri set\>**: Secret used to validate communication within Gitea binary.
- `INTERNAL_TOKEN_URI`: **<empty>**: Instead of defining internal token in the configuration, this configuration option can be used to give Gitea a path to a file that contains the internal token (example value: `file:/etc/gitea/internal_token`)
- `PASSWORD_HASH_ALGO`: **pbkdf2**: The hash algorithm to use \[pbkdf2, argon2, scrypt, bcrypt\].
- `CSRF_COOKIE_HTTP_ONLY`: **true**: Set false to allow JavaScript to read CSRF cookie.
- `PASSWORD_COMPLEXITY`: **lower,upper,digit,spec**: Comma separated list of character classes required to pass minimum complexity. If left empty or no valid values are specified, the default values will be used. Possible values are:
- lower - use one or more lower latin characters
- upper - use one or more upper latin characters
- digit - use one or more digits
- spec - use one or more special characters as ``!"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~``
- off - do not check password complexity
## OpenID (`openid`)
- `ENABLE_OPENID_SIGNIN`: **false**: Allow authentication in via OpenID.
- `ENABLE_OPENID_SIGNUP`: **! DISABLE\_REGISTRATION**: Allow registering via OpenID.
- `WHITELISTED_URIS`: **\<empty\>**: If non-empty, list of POSIX regex patterns matching
OpenID URI's to permit.
- `BLACKLISTED_URIS`: **\<empty\>**: If non-empty, list of POSIX regex patterns matching
OpenID URI's to block.
## Service (`service`)
- `ACTIVE_CODE_LIVE_MINUTES`: **180**: Time limit (min) to confirm account/email registration.
- `RESET_PASSWD_CODE_LIVE_MINUTES`: **180**: Time limit (min) to confirm forgot password reset
process.
- `REGISTER_EMAIL_CONFIRM`: **false**: Enable this to ask for mail confirmation of registration.
Requires `Mailer` to be enabled.
- `DISABLE_REGISTRATION`: **false**: Disable registration, after which only admin can create
accounts for users.
- `REQUIRE_EXTERNAL_REGISTRATION_PASSWORD`: **false**: Enable this to force externally created
accounts (via GitHub, OpenID Connect, etc) to create a password. Warning: enabling this will
decrease security, so you should only enable it if you know what you're doing.
- `REQUIRE_SIGNIN_VIEW`: **false**: Enable this to force users to log in to view any page.
- `ENABLE_NOTIFY_MAIL`: **false**: Enable this to send e-mail to watchers of a repository when
something happens, like creating issues. Requires `Mailer` to be enabled.
- `ENABLE_BASIC_AUTHENTICATION`: **true**: Disable this to disallow authenticaton using HTTP
BASIC and the user's password. Please note if you disable this you will not be able to access the
tokens API endpoints using a password. Further, this only disables BASIC authentication using the
password - not tokens or OAuth Basic.
- `ENABLE_REVERSE_PROXY_AUTHENTICATION`: **false**: Enable this to allow reverse proxy authentication.
- `ENABLE_REVERSE_PROXY_AUTO_REGISTRATION`: **false**: Enable this to allow auto-registration
for reverse authentication.
- `ENABLE_REVERSE_PROXY_EMAIL`: **false**: Enable this to allow to auto-registration with a
provided email rather than a generated email.
- `ENABLE_CAPTCHA`: **false**: Enable this to use captcha validation for registration.
- `REQUIRE_EXTERNAL_REGISTRATION_CAPTCHA`: **false**: Enable this to force captcha validation
even for External Accounts (i.e. GitHub, OpenID Connect, etc). You must `ENABLE_CAPTCHA` also.
- `CAPTCHA_TYPE`: **image**: \[image, recaptcha\]
- `RECAPTCHA_SECRET`: **""**: Go to https://www.google.com/recaptcha/admin to get a secret for recaptcha.
- `RECAPTCHA_SITEKEY`: **""**: Go to https://www.google.com/recaptcha/admin to get a sitekey for recaptcha.
- `RECAPTCHA_URL`: **https://www.google.com/recaptcha/**: Set the recaptcha url - allows the use of recaptcha net.
- `DEFAULT_ENABLE_DEPENDENCIES`: **true**: Enable this to have dependencies enabled by default.
- `ALLOW_CROSS_REPOSITORY_DEPENDENCIES` : **true** Enable this to allow dependencies on issues from any repository where the user is granted access.
- `ENABLE_USER_HEATMAP`: **true**: Enable this to display the heatmap on users profiles.
- `EMAIL_DOMAIN_WHITELIST`: **\<empty\>**: If non-empty, list of domain names that can only be used to register
on this instance.
- `SHOW_REGISTRATION_BUTTON`: **! DISABLE\_REGISTRATION**: Show Registration Button
- `SHOW_MILESTONES_DASHBOARD_PAGE`: **true** Enable this to show the milestones dashboard page - a view of all the user's milestones
- `AUTO_WATCH_NEW_REPOS`: **true**: Enable this to let all organisation users watch new repos when they are created
- `AUTO_WATCH_ON_CHANGES`: **false**: Enable this to make users watch a repository after their first commit to it
- `DEFAULT_ORG_VISIBILITY`: **public**: Set default visibility mode for organisations, either "public", "limited" or "private".
- `DEFAULT_ORG_MEMBER_VISIBLE`: **false** True will make the membership of the users visible when added to the organisation.
- `ALLOW_ONLY_EXTERNAL_REGISTRATION`: **false** Set to true to force registration only using third-party services.
- `NO_REPLY_ADDRESS`: **DOMAIN** Default value for the domain part of the user's email address in the git log if he has set KeepEmailPrivate to true.
The user's email will be replaced with a concatenation of the user name in lower case, "@" and NO_REPLY_ADDRESS.
## Webhook (`webhook`)
- `QUEUE_LENGTH`: **1000**: Hook task queue length. Use caution when editing this value.
- `DELIVER_TIMEOUT`: **5**: Delivery timeout (sec) for shooting webhooks.
- `SKIP_TLS_VERIFY`: **false**: Allow insecure certification.
- `PAGING_NUM`: **10**: Number of webhook history events that are shown in one page.
- `PROXY_URL`: ****: Proxy server URL, support http://, https//, socks://, blank will follow environment http_proxy/https_proxy
- `PROXY_HOSTS`: ****: Comma separated list of host names requiring proxy. Glob patterns (*) are accepted; use ** to match all hosts.
## Mailer (`mailer`)
- `ENABLED`: **false**: Enable to use a mail service.
- `DISABLE_HELO`: **\<empty\>**: Disable HELO operation.
- `HELO_HOSTNAME`: **\<empty\>**: Custom hostname for HELO operation.
- `HOST`: **\<empty\>**: SMTP mail host address and port (example: smtp.gitea.io:587).
- `FROM`: **\<empty\>**: Mail from address, RFC 5322. This can be just an email address, or
the "Name" \<email@example.com\> format.
- `USER`: **\<empty\>**: Username of mailing user (usually the sender's e-mail address).
- `PASSWD`: **\<empty\>**: Password of mailing user. Use \`your password\` for quoting if you use special characters in the password.
- `SKIP_VERIFY`: **\<empty\>**: Do not verify the self-signed certificates.
- **Note:** Gitea only supports SMTP with STARTTLS.
- `SUBJECT_PREFIX`: **\<empty\>**: Prefix to be placed before e-mail subject lines.
- `MAILER_TYPE`: **smtp**: \[smtp, sendmail, dummy\]
- **smtp** Use SMTP to send mail
- **sendmail** Use the operating system's `sendmail` command instead of SMTP.
This is common on linux systems.
- **dummy** Send email messages to the log as a testing phase.
- Note that enabling sendmail will ignore all other `mailer` settings except `ENABLED`,
`FROM`, `SUBJECT_PREFIX` and `SENDMAIL_PATH`.
- Enabling dummy will ignore all settings except `ENABLED`, `SUBJECT_PREFIX` and `FROM`.
- `SENDMAIL_PATH`: **sendmail**: The location of sendmail on the operating system (can be
command or full path).
- ``IS_TLS_ENABLED`` : **false** : Decide if SMTP connections should use TLS.
## Cache (`cache`)
- `ADAPTER`: **memory**: Cache engine adapter, either `memory`, `redis`, or `memcache`.
- `INTERVAL`: **60**: Garbage Collection interval (sec), for memory cache only.
- `HOST`: **\<empty\>**: Connection string for `redis` and `memcache`.
- Redis: `network=tcp,addr=127.0.0.1:6379,password=macaron,db=0,pool_size=100,idle_timeout=180`
- Memcache: `127.0.0.1:9090;127.0.0.1:9091`
- `ITEM_TTL`: **16h**: Time to keep items in cache if not used, Setting it to 0 disables caching.
## Session (`session`)
- `PROVIDER`: **memory**: Session engine provider \[memory, file, redis, mysql, couchbase, memcache, nodb, postgres\].
- `PROVIDER_CONFIG`: **data/sessions**: For file, the root path; for others, the connection string.
- `COOKIE_SECURE`: **false**: Enable this to force using HTTPS for all session access.
- `COOKIE_NAME`: **i\_like\_gitea**: The name of the cookie used for the session ID.
- `GC_INTERVAL_TIME`: **86400**: GC interval in seconds.
## Picture (`picture`)
- `GRAVATAR_SOURCE`: **gravatar**: Can be `gravatar`, `duoshuo` or anything like
`http://cn.gravatar.com/avatar/`.
- `DISABLE_GRAVATAR`: **false**: Enable this to use local avatars only.
- `ENABLE_FEDERATED_AVATAR`: **false**: Enable support for federated avatars (see
[http://www.libravatar.org](http://www.libravatar.org)).
- `AVATAR_UPLOAD_PATH`: **data/avatars**: Path to store user avatar image files.
- `REPOSITORY_AVATAR_UPLOAD_PATH`: **data/repo-avatars**: Path to store repository avatar image files.
- `REPOSITORY_AVATAR_FALLBACK`: **none**: How Gitea deals with missing repository avatars
- none = no avatar will be displayed
- random = random avatar will be generated
- image = default image will be used (which is set in `REPOSITORY_AVATAR_DEFAULT_IMAGE`)
- `REPOSITORY_AVATAR_FALLBACK_IMAGE`: **/img/repo_default.png**: Image used as default repository avatar (if `REPOSITORY_AVATAR_FALLBACK` is set to image and none was uploaded)
- `AVATAR_MAX_WIDTH`: **4096**: Maximum avatar image width in pixels.
- `AVATAR_MAX_HEIGHT`: **3072**: Maximum avatar image height in pixels.
- `AVATAR_MAX_FILE_SIZE`: **1048576** (1Mb): Maximum avatar image file size in bytes.
## Attachment (`attachment`)
- `ENABLED`: **true**: Enable this to allow uploading attachments.
- `PATH`: **data/attachments**: Path to store attachments.
- `ALLOWED_TYPES`: **see app.ini.sample**: Allowed MIME types, e.g. `image/jpeg|image/png`.
Use `*/*` for all types.
- `MAX_SIZE`: **4**: Maximum size (MB).
- `MAX_FILES`: **5**: Maximum number of attachments that can be uploaded at once.
## Log (`log`)
- `ROOT_PATH`: **\<empty\>**: Root path for log files.
- `MODE`: **console**: Logging mode. For multiple modes, use a comma to separate values. You can configure each mode in per mode log subsections `\[log.modename\]`. By default the file mode will log to `$ROOT_PATH/gitea.log`.
- `LEVEL`: **Info**: General log level. \[Trace, Debug, Info, Warn, Error, Critical, Fatal, None\]
- `STACKTRACE_LEVEL`: **None**: Default log level at which to log create stack traces. \[Trace, Debug, Info, Warn, Error, Critical, Fatal, None\]
- `REDIRECT_MACARON_LOG`: **false**: Redirects the Macaron log to its own logger or the default logger.
- `MACARON`: **file**: Logging mode for the macaron logger, use a comma to separate values. Configure each mode in per mode log subsections `\[log.modename.macaron\]`. By default the file mode will log to `$ROOT_PATH/macaron.log`. (If you set this to `,` it will log to default gitea logger.)
- `ROUTER_LOG_LEVEL`: **Info**: The log level that the router should log at. (If you are setting the access log, its recommended to place this at Debug.)
- `ROUTER`: **console**: The mode or name of the log the router should log to. (If you set this to `,` it will log to default gitea logger.)
NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false` for this option to take effect. Configure each mode in per mode log subsections `\[log.modename.router\]`.
- `ENABLE_ACCESS_LOG`: **false**: Creates an access.log in NCSA common log format, or as per the following template
- `ACCESS`: **file**: Logging mode for the access logger, use a comma to separate values. Configure each mode in per mode log subsections `\[log.modename.access\]`. By default the file mode will log to `$ROOT_PATH/access.log`. (If you set this to `,` it will log to the default gitea logger.)
- `ACCESS_LOG_TEMPLATE`: **`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`**: Sets the template used to create the access log.
- The following variables are available:
- `Ctx`: the `macaron.Context` of the request.
- `Identity`: the SignedUserName or `"-"` if not logged in.
- `Start`: the start time of the request.
- `ResponseWriter`: the responseWriter from the request.
- You must be very careful to ensure that this template does not throw errors or panics as this template runs outside of the panic/recovery script.
- `ENABLE_XORM_LOG`: **true**: Set whether to perform XORM logging. Please note SQL statement logging can be disabled by setting `LOG_SQL` to false in the `[database]` section.
### Log subsections (`log.name`, `log.name.*`)
- `LEVEL`: **log.LEVEL**: Sets the log-level of this sublogger. Defaults to the `LEVEL` set in the global `[log]` section.
- `STACKTRACE_LEVEL`: **log.STACKTRACE_LEVEL**: Sets the log level at which to log stack traces.
- `MODE`: **name**: Sets the mode of this sublogger - Defaults to the provided subsection name. This allows you to have two different file loggers at different levels.
- `EXPRESSION`: **""**: A regular expression to match either the function name, file or message. Defaults to empty. Only log messages that match the expression will be saved in the logger.
- `FLAGS`: **stdflags**: A comma separated string representing the log flags. Defaults to `stdflags` which represents the prefix: `2009/01/23 01:23:23 ...a/b/c/d.go:23:runtime.Caller() [I]: message`. `none` means don't prefix log lines. See `modules/log/base.go` for more information.
- `PREFIX`: **""**: An additional prefix for every log line in this logger. Defaults to empty.
- `COLORIZE`: **false**: Colorize the log lines by default
### Console log mode (`log.console`, `log.console.*`, or `MODE=console`)
- For the console logger `COLORIZE` will default to `true` if not on windows or the terminal is determined to be able to color.
- `STDERR`: **false**: Use Stderr instead of Stdout.
### File log mode (`log.file`, `log.file.*` or `MODE=file`)
- `FILE_NAME`: Set the file name for this logger. Defaults as described above. If relative will be relative to the `ROOT_PATH`
- `LOG_ROTATE`: **true**: Rotate the log files.
- `MAX_SIZE_SHIFT`: **28**: Maximum size shift of a single file, 28 represents 256Mb.
- `DAILY_ROTATE`: **true**: Rotate logs daily.
- `MAX_DAYS`: **7**: Delete the log file after n days
- `COMPRESS`: **true**: Compress old log files by default with gzip
- `COMPRESSION_LEVEL`: **-1**: Compression level
### Conn log mode (`log.conn`, `log.conn.*` or `MODE=conn`)
- `RECONNECT_ON_MSG`: **false**: Reconnect host for every single message.
- `RECONNECT`: **false**: Try to reconnect when connection is lost.
- `PROTOCOL`: **tcp**: Set the protocol, either "tcp", "unix" or "udp".
- `ADDR`: **:7020**: Sets the address to connect to.
### SMTP log mode (`log.smtp`, `log.smtp.*` or `MODE=smtp`)
- `USER`: User email address to send from.
- `PASSWD`: Password for the smtp server.
- `HOST`: **127.0.0.1:25**: The SMTP host to connect to.
- `RECEIVERS`: Email addresses to send to.
- `SUBJECT`: **Diagnostic message from Gitea**
## Cron (`cron`)
- `ENABLED`: **true**: Run cron tasks periodically.
- `RUN_AT_START`: **false**: Run cron tasks at application start-up.
### Cron - Cleanup old repository archives (`cron.archive_cleanup`)
- `ENABLED`: **true**: Enable service.
- `RUN_AT_START`: **true**: Run tasks at start up time (if ENABLED).
- `SCHEDULE`: **@every 24h**: Cron syntax for scheduling repository archive cleanup, e.g. `@every 1h`.
- `OLDER_THAN`: **24h**: Archives created more than `OLDER_THAN` ago are subject to deletion, e.g. `12h`.
### Cron - Update Mirrors (`cron.update_mirrors`)
- `SCHEDULE`: **@every 10m**: Cron syntax for scheduling update mirrors, e.g. `@every 3h`.
### Cron - Repository Health Check (`cron.repo_health_check`)
- `SCHEDULE`: **every 24h**: Cron syntax for scheduling repository health check.
- `TIMEOUT`: **60s**: Time duration syntax for health check execution timeout.
- `ARGS`: **\<empty\>**: Arguments for command `git fsck`, e.g. `--unreachable --tags`. See more on http://git-scm.com/docs/git-fsck
### Cron - Repository Statistics Check (`cron.check_repo_stats`)
- `RUN_AT_START`: **true**: Run repository statistics check at start time.
- `SCHEDULE`: **@every 24h**: Cron syntax for scheduling repository statistics check.
### Cron - Update Migration Poster ID (`cron.update_migration_post_id`)
- `SCHEDULE`: **@every 24h** : Interval as a duration between each synchronization, it will always attempt synchronization when the instance starts.
## Git (`git`)
- `PATH`: **""**: The path of git executable. If empty, Gitea searches through the PATH environment.
- `MAX_GIT_DIFF_LINES`: **100**: Max number of lines allowed of a single file in diff view.
- `MAX_GIT_DIFF_LINE_CHARACTERS`: **5000**: Max character count per line highlighted in diff view.
- `MAX_GIT_DIFF_FILES`: **100**: Max number of files shown in diff view.
- `GC_ARGS`: **\<empty\>**: Arguments for command `git gc`, e.g. `--aggressive --auto`. See more on http://git-scm.com/docs/git-gc/
- `ENABLE_AUTO_GIT_WIRE_PROTOCOL`: **true**: If use git wire protocol version 2 when git version >= 2.18, default is true, set to false when you always want git wire protocol version 1
## Git - Timeout settings (`git.timeout`)
- `DEFAUlT`: **360**: Git operations default timeout seconds.
- `MIGRATE`: **600**: Migrate external repositories timeout seconds.
- `MIRROR`: **300**: Mirror external repositories timeout seconds.
- `CLONE`: **300**: Git clone from internal repositories timeout seconds.
- `PULL`: **300**: Git pull from internal repositories timeout seconds.
- `GC`: **60**: Git repository GC timeout seconds.
## Metrics (`metrics`)
- `ENABLED`: **false**: Enables /metrics endpoint for prometheus.
- `TOKEN`: **\<empty\>**: You need to specify the token, if you want to include in the authorization the metrics . The same token need to be used in prometheus parameters `bearer_token` or `bearer_token_file`.
## API (`api`)
- `ENABLE_SWAGGER`: **true**: Enables /api/swagger, /api/v1/swagger etc. endpoints. True or false; default is true.
- `MAX_RESPONSE_ITEMS`: **50**: Max number of items in a page.
- `DEFAULT_PAGING_NUM`: **30**: Default paging number of API.
- `DEFAULT_GIT_TREES_PER_PAGE`: **1000**: Default and maximum number of items per page for git trees API.
- `DEFAULT_MAX_BLOB_SIZE`: **10485760**: Default max size of a blob that can be return by the blobs API.
## OAuth2 (`oauth2`)
- `ENABLE`: **true**: Enables OAuth2 provider.
- `ACCESS_TOKEN_EXPIRATION_TIME`: **3600**: Lifetime of an OAuth2 access token in seconds
- `REFRESH_TOKEN_EXPIRATION_TIME`: **730**: Lifetime of an OAuth2 access token in hours
- `INVALIDATE_REFRESH_TOKEN`: **false**: Check if refresh token got already used
- `JWT_SECRET`: **\<empty\>**: OAuth2 authentication secret for access and refresh tokens, change this a unique string.
## i18n (`i18n`)
- `LANGS`: **en-US,zh-CN,zh-HK,zh-TW,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT,fi-FI,tr-TR,cs-CZ,sr-SP,sv-SE,ko-KR**: List of locales shown in language selector
- `NAMES`: **English,简体中文,繁體中文(香港),繁體中文(台灣),Deutsch,français,Nederlands,latviešu,русский,日本語,español,português do Brasil,polski,български,italiano,suomi,Türkçe,čeština,српски,svenska,한국어**: Visible names corresponding to the locales
### i18n - Datepicker Language (`i18n.datelang`)
Maps locales to the languages used by the datepicker plugin
- `en-US`: **en**
- `zh-CN`: **zh**
- `zh-HK`: **zh-HK**
- `zh-TW`: **zh-TW**
- `de-DE`: **de**
- `fr-FR`: **fr**
- `nl-NL`: **nl**
- `lv-LV`: **lv**
- `ru-RU`: **ru**
- `ja-JP`: **ja**
- `es-ES`: **es**
- `pt-BR`: **pt-BR**
- `pl-PL`: **pl**
- `bg-BG`: **bg**
- `it-IT`: **it**
- `fi-FI`: **fi**
- `tr-TR`: **tr**
- `cs-CZ`: **cs-CZ**
- `sr-SP`: **sr**
- `sv-SE`: **sv**
- `ko-KR`: **ko**
## U2F (`U2F`)
- `APP_ID`: **`ROOT_URL`**: Declares the facet of the application. Requires HTTPS.
- `TRUSTED_FACETS`: List of additional facets which are trusted. This is not support by all browsers.
## Markup (`markup`)
Gitea can support Markup using external tools. The example below will add a markup named `asciidoc`.
```ini
[markup.asciidoc]
ENABLED = true
FILE_EXTENSIONS = .adoc,.asciidoc
RENDER_COMMAND = "asciidoc --out-file=- -"
IS_INPUT_FILE = false
```
- ENABLED: **false** Enable markup support; set to **true** to enable this renderer.
- FILE\_EXTENSIONS: **\<empty\>** List of file extensions that should be rendered by an external
command. Multiple extentions needs a comma as splitter.
- RENDER\_COMMAND: External command to render all matching extensions.
- IS\_INPUT\_FILE: **false** Input is not a standard input but a file param followed `RENDER_COMMAND`.
Two special environment variables are passed to the render command:
- `GITEA_PREFIX_SRC`, which contains the current URL prefix in the `src` path tree. To be used as prefix for links.
- `GITEA_PREFIX_RAW`, which contains the current URL prefix in the `raw` path tree. To be used as prefix for image paths.
Gitea supports customizing the sanitization policy for rendered HTML. The example below will support KaTeX output from pandoc.
```ini
[markup.sanitizer]
; Pandoc renders TeX segments as <span>s with the "math" class, optionally
; with "inline" or "display" classes depending on context.
ELEMENT = span
ALLOW_ATTR = class
REGEXP = ^\s*((math(\s+|$)|inline(\s+|$)|display(\s+|$)))+
```
- `ELEMENT`: The element this policy applies to. Must be non-empty.
- `ALLOW_ATTR`: The attribute this policy allows. Must be non-empty.
- `REGEXP`: A regex to match the contents of the attribute against. Must be present but may be empty for unconditional whitelisting of this attribute.
You may redefine `ELEMENT`, `ALLOW_ATTR`, and `REGEXP` multiple times; each time all three are defined is a single policy entry.
## Time (`time`)
- `FORMAT`: Time format to diplay on UI. i.e. RFC1123 or 2006-01-02 15:04:05
- `DEFAULT_UI_LOCATION`: Default location of time on the UI, so that we can display correct user's time on UI. i.e. Shanghai/Asia
## Task (`task`)
- Task queue configuration has been moved to `queue.task` however, the below configuration values are kept for backwards compatibilityx:
- `QUEUE_TYPE`: **channel**: Task queue type, could be `channel` or `redis`.
- `QUEUE_LENGTH`: **1000**: Task queue length, available only when `QUEUE_TYPE` is `channel`.
- `QUEUE_CONN_STR`: **addrs=127.0.0.1:6379 db=0**: Task queue connection string, available only when `QUEUE_TYPE` is `redis`. If there redis needs a password, use `addrs=127.0.0.1:6379 password=123 db=0`.
## Migrations (`migrations`)
- `MAX_ATTEMPTS`: **3**: Max attempts per http/https request on migrations.
- `RETRY_BACKOFF`: **3**: Backoff time per http/https request retry (seconds)
## Other (`other`)
- `SHOW_FOOTER_BRANDING`: **false**: Show Gitea branding in the footer.
- `SHOW_FOOTER_VERSION`: **true**: Show Gitea version information in the footer.
- `SHOW_FOOTER_TEMPLATE_LOAD_TIME`: **true**: Show time of template execution in the footer.

View File

@@ -1,284 +0,0 @@
---
date: "2016-12-26T16:00:00+02:00"
title: "配置说明"
slug: "config-cheat-sheet"
weight: 20
toc: false
draft: false
menu:
sidebar:
parent: "advanced"
name: "配置说明"
weight: 20
identifier: "config-cheat-sheet"
---
# 配置说明
这是针对Gitea配置文件的说明你可以了解Gitea的强大配置。需要说明的是你的所有改变请修改 `custom/conf/app.ini` 文件而不是源文件。所有默认值可以通过 [app.ini.sample](https://github.com/go-gitea/gitea/blob/master/custom/conf/app.ini.sample) 查看到。如果你发现 `%(X)s` 这样的内容,请查看 [ini](https://github.com/go-ini/ini/#recursive-values) 这里的说明。标注了 :exclamation: 的配置项表明除非你真的理解这个配置项的意义,否则最好使用默认值。
## Overall (`DEFAULT`)
- `APP_NAME`: 应用名称,改成你希望的名字。
- `RUN_USER`: 运行Gitea的用户推荐使用 `git`如果在你自己的个人电脑使用改成你自己的用户名。如果设置不正确Gitea可能崩溃。
- `RUN_MODE`: 从性能考虑,如果在产品级的服务上改成 `prod`。如果您使用安装向导安装的那么会自动设置为 `prod`
## Repository (`repository`)
- `ROOT`: 存放git工程的根目录。这里必须填绝对路径默认值是 `~/<username>/gitea-repositories`
- `SCRIPT_TYPE`: 服务器支持的Shell类型通常是 `bash`,但有些服务器也有可能是 `sh`
- `ANSI_CHARSET`: 默认字符编码。
- `FORCE_PRIVATE`: 强制所有git工程必须私有。
- `DEFAULT_PRIVATE`: 默认创建的git工程为私有。 可以是`last`, `private``public`。默认值是 `last`表示用户最后创建的Repo的选择。
- `MAX_CREATION_LIMIT`: 全局最大每个用户创建的git工程数目 `-1` 表示没限制。
- `PULL_REQUEST_QUEUE_LENGTH`: 小心:合并请求测试队列的长度,尽量放大。
## UI (`ui`)
- `EXPLORE_PAGING_NUM`: 探索页面每页显示的仓库数量。
- `ISSUE_PAGING_NUM`: 工单页面每页显示的工单数量。
- `MEMBERS_PAGING_NUM`: **20**: 组织成员页面每页显示的成员数量。
- `FEED_MAX_COMMIT_NUM`: 活动流页面显示的最大提交数量。
### UI - Admin (`ui.admin`)
- `USER_PAGING_NUM`: 用户管理页面每页显示的用户数量。
- `REPO_PAGING_NUM`: 仓库管理页面每页显示的仓库数量。
- `NOTICE_PAGING_NUM`: 系统提示页面每页显示的提示数量。
- `ORG_PAGING_NUM`: 组织管理页面每页显示的组织数量。
## Markdown (`markdown`)
- `ENABLE_HARD_LINE_BREAK`: 是否启用硬换行扩展。
## Server (`server`)
- `PROTOCOL`: 可选 `http``https`
- `DOMAIN`: 服务器域名。
- `ROOT_URL`: Gitea服务器的对外 URL。
- `HTTP_ADDR`: HTTP 监听地址。
- `HTTP_PORT`: HTTP 监听端口。
- `DISABLE_SSH`: 是否禁用SSH。
- `START_SSH_SERVER`: 是否启用内部SSH服务器。
- `SSH_PORT`: SSH端口默认为 `22`
- `OFFLINE_MODE`: 针对静态和头像文件禁用 CDN。
- `DISABLE_ROUTER_LOG`: 关闭日志中的路由日志。
- `CERT_FILE`: 启用HTTPS的证书文件。
- `KEY_FILE`: 启用HTTPS的密钥文件。
- `STATIC_ROOT_PATH`: 存放模板和静态文件的根目录,默认是 Gitea 的根目录。
- `STATIC_CACHE_TIME`: **6h**: 静态资源文件,包括 `custom/`, `public/` 和所有上传的头像的浏览器缓存时间。
- `ENABLE_GZIP`: 启用应用级别的 GZIP 压缩。
- `LANDING_PAGE`: 未登录用户的默认页面,可选 `home``explore`
- `LFS_START_SERVER`: 是否启用 git-lfs 支持. 可以为 `true``false` 默认是 `false`
- `LFS_CONTENT_PATH`: 存放 lfs 命令上传的文件的地方,默认是 `data/lfs`
- `LFS_JWT_SECRET`: LFS 认证密钥,改成自己的。
## Database (`database`)
- `DB_TYPE`: 数据库类型,可选 `mysql`, `postgres`, `mssql`, `tidb``sqlite3`
- `HOST`: 数据库服务器地址和端口。
- `NAME`: 数据库名称。
- `USER`: 数据库用户名。
- `PASSWD`: 数据库用户密码。
- `SSL_MODE`: MySQL 或 PostgreSQL数据库是否启用SSL模式。
- `CHARSET`: **utf8**: 仅当数据库为 MySQL 时有效, 可以为 "utf8" 或 "utf8mb4"。注意:如果使用 "utf8mb4",你的 MySQL InnoDB 版本必须在 5.6 以上。
- `PATH`: Tidb 或者 SQLite3 数据文件存放路径。
- `LOG_SQL`: **true**: 显示生成的SQL默认为真。
- `MAX_IDLE_CONNS` **0**: 最大空闲数据库连接
- `CONN_MAX_LIFETIME` **3s**: 数据库连接最大存活时间
## Indexer (`indexer`)
- `ISSUE_INDEXER_TYPE`: **bleve**: 工单索引类型,当前支持 `bleve``db`,当为 `db` 时其它工单索引项可不用设置。
- `ISSUE_INDEXER_PATH`: **indexers/issues.bleve**: 工单索引文件存放路径,当索引类型为 `bleve` 时有效。
- `ISSUE_INDEXER_QUEUE_TYPE`: **levelqueue**: 工单索引队列类型,当前支持 `channel` `levelqueue``redis`
- `ISSUE_INDEXER_QUEUE_DIR`: **indexers/issues.queue**: 当 `ISSUE_INDEXER_QUEUE_TYPE``levelqueue` 时,保存索引队列的磁盘路径。
- `ISSUE_INDEXER_QUEUE_CONN_STR`: **addrs=127.0.0.1:6379 db=0**: 当 `ISSUE_INDEXER_QUEUE_TYPE``redis`保存Redis队列的连接字符串。
- `ISSUE_INDEXER_QUEUE_BATCH_NUMBER`: **20**: 队列处理中批量提交数量。
- `REPO_INDEXER_ENABLED`: **false**: 是否启用代码搜索(启用后会占用比较大的磁盘空间)。
- `REPO_INDEXER_PATH`: **indexers/repos.bleve**: 用于代码搜索的索引文件路径。
- `UPDATE_BUFFER_LEN`: **20**: 代码索引请求的缓冲区长度。
- `MAX_FILE_SIZE`: **1048576**: 进行解析的源代码文件的最大长度,小于该值时才会索引。
## Security (`security`)
- `INSTALL_LOCK`: 是否允许运行安装向导,(跟管理员账号有关,十分重要)。
- `SECRET_KEY`: 全局服务器安全密钥 **最好改成你自己的** (当你运行安装向导的时候会被设置为一个随机值)。
- `LOGIN_REMEMBER_DAYS`: Cookie 保存时间,单位天。
- `COOKIE_USERNAME`: 保存用户名的 cookie 名称。
- `COOKIE_REMEMBER_NAME`: 保存自动登录信息的 cookie 名称。
- `REVERSE_PROXY_AUTHENTICATION_USER`: 反向代理认证的 HTTP 头名称。
## Service (`service`)
- `ACTIVE_CODE_LIVE_MINUTES`: 登录验证码失效时间,单位分钟。
- `RESET_PASSWD_CODE_LIVE_MINUTES`: 重置密码失效时间,单位分钟。
- `REGISTER_EMAIL_CONFIRM`: 启用注册邮件激活,前提是 `Mailer` 已经启用。
- `DISABLE_REGISTRATION`: 禁用注册,启用后只能用管理员添加用户。
- `SHOW_REGISTRATION_BUTTON`: 是否显示注册按钮。
- `REQUIRE_SIGNIN_VIEW`: 是否所有页面都必须登录后才可访问。
- `ENABLE_CACHE_AVATAR`: 是否缓存来自 Gravatar 的头像。
- `ENABLE_NOTIFY_MAIL`: 是否发送工单创建等提醒邮件,需要 `Mailer` 被激活。
- `ENABLE_REVERSE_PROXY_AUTHENTICATION`: 允许反向代理认证更多细节见https://github.com/gogits/gogs/issues/165
- `ENABLE_REVERSE_PROXY_AUTO_REGISTRATION`: 允许通过反向认证做自动注册。
- `ENABLE_CAPTCHA`: 注册时使用图片验证码。
## Webhook (`webhook`)
- `QUEUE_LENGTH`: 说明: Hook 任务队列长度。
- `DELIVER_TIMEOUT`: 请求webhooks的超时时间单位秒。
- `SKIP_TLS_VERIFY`: 是否允许不安全的证书。
- `PAGING_NUM`: 每页显示的Webhook 历史数量。
- `PROXY_URL`: ****: 代理服务器网址,支持 http://, https//, socks://, 为空将使用环境变量中的 http_proxy/https_proxy 设置。
- `PROXY_HOSTS`: ****: 逗号分隔的需要代理的域名或IP地址。支持 * 号匹配符,使用 ** 匹配所有域名和IP地址。
## Mailer (`mailer`)
- `ENABLED`: 是否启用邮件服务。
- `DISABLE_HELO`: 禁用 HELO 命令。
- `HELO_HOSTNAME`: 自定义主机名来回应 HELO 命令。
- `HOST`: SMTP 主机地址和端口 (例如smtp.gitea.io:587)。
- `FROM`: 邮件发送地址RFC 5322. 这里可以填一个邮件地址或者 "Name" \<email@example.com\> 格式。
- `USER`: 用户名(通常就是邮件地址)。
- `PASSWD`: 密码。
- `SKIP_VERIFY`: 忽略证书验证。
说明:实际上 Gitea 仅仅支持基于 STARTTLS 的 SMTP。
## Cache (`cache`)
- `ADAPTER`: **memory**: 缓存引擎,可以为 `memory`, `redis``memcache`
- `INTERVAL`: **60**: 只对内存缓存有效GC间隔单位秒。
- `HOST`: **\<empty\>**: 针对redis和memcache有效主机地址和端口。
- Redis: `network=tcp,addr=127.0.0.1:6379,password=macaron,db=0,pool_size=100,idle_timeout=180`
- Memache: `127.0.0.1:9090;127.0.0.1:9091`
- `ITEM_TTL`: **16h**: 缓存项目失效时间,设置为 0 则禁用缓存。
## Session (`session`)
- `PROVIDER`: Session 内容存储方式,可选 `memory`, `file`, `redis``mysql`
- `PROVIDER_CONFIG`: 如果是文件,那么这里填根目录;其他的要填主机地址和端口。
- `COOKIE_SECURE`: 强制使用 HTTPS 作为session访问。
- `GC_INTERVAL_TIME`: Session失效时间。
## Picture (`picture`)
- `GRAVATAR_SOURCE`: 头像来源,可以是 `gravatar`, `duoshuo` 或者类似 `http://cn.gravatar.com/avatar/` 的来源
- `DISABLE_GRAVATAR`: 开启则只使用内部头像。
- `ENABLE_FEDERATED_AVATAR`: 启用头像联盟支持 (参见 http://www.libravatar.org)
## Attachment (`attachment`)
- `ENABLED`: 是否允许用户上传附件。
- `PATH`: 附件存储路径
- `ALLOWED_TYPES`: 允许上传的附件类型。比如:`image/jpeg|image/png`,用 `*/*` 表示允许任何类型。
- `MAX_SIZE`: 附件最大限制,单位 MB比如 `4`
- `MAX_FILES`: 一次最多上传的附件数量,比如: `5`
关于 `ALLOWED_TYPES` 在 (*)unix 系统中可以使用`file -I <filename>` 来快速获得对应的 `MIME type`
```shell
$ file -I test00.tar.xz
test00.tar.xz: application/x-xz; charset=binary
$ file --mime test00.xlsx
test00.xlsx: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=binary
file -I test01.xls
test01.xls: application/vnd.ms-excel; charset=binary
```
## Log (`log`)
- `ROOT_PATH`: 日志文件根目录。
- `MODE`: 日志记录模式,默认是为 `console`。如果要写到多个通道,用逗号分隔
- `LEVEL`: 日志级别,默认为`Trace`
## Cron (`cron`)
- `ENABLED`: 是否在后台运行定期任务。
- `RUN_AT_START`: 是否启动时自动运行。
### Cron - Update Mirrors (`cron.update_mirrors`)
- `SCHEDULE`: 自动同步镜像仓库的Cron语法比如`@every 1h`
### Cron - Repository Health Check (`cron.repo_health_check`)
- `SCHEDULE`: 仓库健康监测的Cron语法比如`@every 24h`
- `TIMEOUT`: 仓库健康监测的超时时间,比如:`60s`.
- `ARGS`: 执行 `git fsck` 命令的参数,比如:`--unreachable --tags`
### Cron - Repository Statistics Check (`cron.check_repo_stats`)
- `RUN_AT_START`: 是否启动时自动运行仓库统计。
- `SCHEDULE`: 仓库统计时的Cron 语法,比如:`@every 24h`.
### Cron - Update Migration Poster ID (`cron.update_migration_post_id`)
- `SCHEDULE`: **@every 24h** : 每次同步的间隔时间。此任务总是在启动时自动进行。
## Git (`git`)
- `MAX_GIT_DIFF_LINES`: 比较视图中,一个文件最多显示行数。
- `MAX_GIT_DIFF_LINE_CHARACTERS`: 比较视图中一行最大字符数。
- `MAX_GIT_DIFF_FILES`: 比较视图中的最大现实文件数目。
- `GC_ARGS`: 执行 `git gc` 命令的参数, 比如: `--aggressive --auto`
## Git - 超时设置 (`git.timeout`)
- `DEFAUlT`: **360**: Git操作默认超时时间单位秒
- `MIGRATE`: **600**: 迁移外部仓库时的超时时间,单位秒
- `MIRROR`: **300**: 镜像外部仓库的超时时间,单位秒
- `CLONE`: **300**: 内部仓库间克隆的超时时间,单位秒
- `PULL`: **300**: 内部仓库间拉取的超时时间,单位秒
- `GC`: **60**: git仓库GC的超时时间单位秒
- `ENABLE_AUTO_GIT_WIRE_PROTOCOL`: **true**: 是否根据 Git Wire Protocol协议支持情况自动切换版本当 git 版本在 2.18 及以上时会自动切换到版本2。为 `false` 则不切换。
## API (`api`)
- `ENABLE_SWAGGER`: **true**: 是否启用swagger路由 /api/swagger, /api/v1/swagger etc. endpoints. True 或 false; 默认是 true.
- `MAX_RESPONSE_ITEMS`: **50**: 一个页面最大的项目数。
- `DEFAULT_PAGING_NUM`: **30**: API中默认分页条数。
- `DEFAULT_GIT_TREES_PER_PAGE`: **1000**: GIT TREES API每页的默认最大项数.
- `DEFAULT_MAX_BLOB_SIZE`: **10485760**: BLOBS API默认最大大小.
## Markup (`markup`)
外部渲染工具支持,你可以用你熟悉的文档渲染工具. 比如一下将新增一个名字为 `asciidoc` 的渲染工具which is followed `markup.` ini section. And there are some config items below.
```ini
[markup.asciidoc]
ENABLED = false
FILE_EXTENSIONS = .adoc,.asciidoc
RENDER_COMMAND = "asciidoc --out-file=- -"
IS_INPUT_FILE = false
```
- ENABLED: 是否启用默认为false。
- FILE_EXTENSIONS: 关联的文档的扩展名,多个扩展名用都好分隔。
- RENDER_COMMAND: 工具的命令行命令及参数。
- IS_INPUT_FILE: 输入方式是最后一个参数为文件路径还是从标准输入读取。
## Time (`time`)
- `FORMAT`: 显示在界面上的时间格式。比如: RFC1123 或者 2006-01-02 15:04:05
- `DEFAULT_UI_LOCATION`: 默认显示在界面上的时区,默认为本地时区。比如: Asia/Shanghai
## Task (`task`)
- `QUEUE_TYPE`: **channel**: 任务队列类型,可以为 `channel``redis`
- `QUEUE_LENGTH`: **1000**: 任务队列长度,当 `QUEUE_TYPE``channel` 时有效。
- `QUEUE_CONN_STR`: **addrs=127.0.0.1:6379 db=0**: 任务队列连接字符串,当 `QUEUE_TYPE``redis` 时有效。如果redis有密码则可以 `addrs=127.0.0.1:6379 password=123 db=0`
## Migrations (`migrations`)
- `MAX_ATTEMPTS`: **3**: 在迁移过程中的 http/https 请求重试次数。
- `RETRY_BACKOFF`: **3**: 等待下一次重试的时间,单位秒。
## Other (`other`)
- `SHOW_FOOTER_BRANDING`: 为真则在页面底部显示Gitea的字样。
- `SHOW_FOOTER_VERSION`: 为真则在页面底部显示Gitea的版本。

Some files were not shown because too many files have changed in this diff Show More