mirror of
https://github.com/go-gitea/gitea.git
synced 2026-04-19 22:11:00 +00:00
Compare commits
203 Commits
v1.25.0-de
...
v1.20.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09814117e3 | ||
|
|
499c5594c3 | ||
|
|
ecfbcced46 | ||
|
|
892e24aaf1 | ||
|
|
666038a06d | ||
|
|
54614767a2 | ||
|
|
e42c5afadb | ||
|
|
72b55c8094 | ||
|
|
a12d036a68 | ||
|
|
65d6bdf0be | ||
|
|
c598741f01 | ||
|
|
bc73e6a85c | ||
|
|
a8445e9320 | ||
|
|
0f73be0ae3 | ||
|
|
4033d95dbf | ||
|
|
43213b816d | ||
|
|
a55924aaf4 | ||
|
|
782b137682 | ||
|
|
5992365fc1 | ||
|
|
3b518a3af5 | ||
|
|
08cdc0da3d | ||
|
|
3e07c54be3 | ||
|
|
e2596b0a99 | ||
|
|
a424f6d4f8 | ||
|
|
59713541b6 | ||
|
|
8d9193680d | ||
|
|
ab4fd9aa1f | ||
|
|
221b90d289 | ||
|
|
81f5a87eb4 | ||
|
|
8b002b429d | ||
|
|
dfd371a363 | ||
|
|
54a516e9da | ||
|
|
ac129d4b4c | ||
|
|
4d5e3b9372 | ||
|
|
1ba0baa030 | ||
|
|
28e8c691a6 | ||
|
|
f81a612eb1 | ||
|
|
ee47face12 | ||
|
|
864bdd0ac8 | ||
|
|
037a3f0d8c | ||
|
|
227c3b67e0 | ||
|
|
5c3662b902 | ||
|
|
ab54310731 | ||
|
|
b7d054e4b5 | ||
|
|
d032500687 | ||
|
|
9159964ada | ||
|
|
9369b38315 | ||
|
|
6e82d0bb7c | ||
|
|
36b9a86bd8 | ||
|
|
e627f161c2 | ||
|
|
de8127e78b | ||
|
|
f7e271ff85 | ||
|
|
186f07bbf7 | ||
|
|
45b1f4dd3b | ||
|
|
026e745b9e | ||
|
|
c334be8284 | ||
|
|
353dcc5ad4 | ||
|
|
7811027ca1 | ||
|
|
abe9c641ce | ||
|
|
052e65e63f | ||
|
|
c1a10be07e | ||
|
|
2b79d3fd52 | ||
|
|
b4460cf541 | ||
|
|
a1bc2aa05e | ||
|
|
d713cf6150 | ||
|
|
012b804a9a | ||
|
|
372b622c2b | ||
|
|
06bcdfe77a | ||
|
|
a5a3c81412 | ||
|
|
ea2c9de3c4 | ||
|
|
348a6bf70d | ||
|
|
68a3961bf1 | ||
|
|
91dadedddf | ||
|
|
32eaba1b40 | ||
|
|
582dcaa12e | ||
|
|
917ca5ded9 | ||
|
|
e595dfeec7 | ||
|
|
03cacf971e | ||
|
|
68e0c802f7 | ||
|
|
09668b2e2e | ||
|
|
04eea29ecb | ||
|
|
511be9fe6e | ||
|
|
24e64fe372 | ||
|
|
4e310133f9 | ||
|
|
491f36d32a | ||
|
|
9111d2d037 | ||
|
|
5510ed34f1 | ||
|
|
39fce5750d | ||
|
|
1f90376041 | ||
|
|
0af6542a34 | ||
|
|
69bdcf41f3 | ||
|
|
e610b0389a | ||
|
|
13ffa287b1 | ||
|
|
e5b684e567 | ||
|
|
64ed262e18 | ||
|
|
f51c8e0008 | ||
|
|
d8a59d5f12 | ||
|
|
1ddfe03131 | ||
|
|
24cf06592e | ||
|
|
0b6f7fb607 | ||
|
|
c27a3af728 | ||
|
|
12aca3ef20 | ||
|
|
2390a46d0f | ||
|
|
51b6a78791 | ||
|
|
e6f62eea70 | ||
|
|
8981f6d0fc | ||
|
|
b2b5c80cb2 | ||
|
|
77db40e084 | ||
|
|
7222bac4e3 | ||
|
|
0eb4ab4246 | ||
|
|
102dcfa3a0 | ||
|
|
614d6df2d8 | ||
|
|
345a25d016 | ||
|
|
e8a7cd4a1d | ||
|
|
6c43b9f6f6 | ||
|
|
40744f8976 | ||
|
|
9d69a4758e | ||
|
|
53747a58a0 | ||
|
|
00ba826360 | ||
|
|
9bbb4d8d6d | ||
|
|
5703a0d3e3 | ||
|
|
85bad22ff8 | ||
|
|
71d2a6a41a | ||
|
|
d1f1f1142e | ||
|
|
2cd9d6b3f9 | ||
|
|
050c38ca19 | ||
|
|
51789ba12d | ||
|
|
b0de3d08b8 | ||
|
|
2e64449de7 | ||
|
|
ec539b7a77 | ||
|
|
6fbdacb524 | ||
|
|
948f6ca029 | ||
|
|
061b68e995 | ||
|
|
734fd93f59 | ||
|
|
203fe2841d | ||
|
|
056829749e | ||
|
|
f18b8e7d8a | ||
|
|
ea00ed320d | ||
|
|
30a783879f | ||
|
|
cb3173a1e9 | ||
|
|
ffe089432f | ||
|
|
8302b95d6b | ||
|
|
6f1c95ec5b | ||
|
|
cda69a0363 | ||
|
|
4908cc9adf | ||
|
|
28ed763f55 | ||
|
|
8e89eb8f43 | ||
|
|
dfefe86045 | ||
|
|
10fcb55507 | ||
|
|
e9105ac281 | ||
|
|
e6e1cfd8e4 | ||
|
|
072997692c | ||
|
|
e9fab3ea3e | ||
|
|
e0bd6ebabd | ||
|
|
cc73f6e821 | ||
|
|
ff18c3ba65 | ||
|
|
b673edbeaf | ||
|
|
05431593ef | ||
|
|
aa4c9c3215 | ||
|
|
4e79c76ed0 | ||
|
|
3bd311c3f4 | ||
|
|
7e06e6a042 | ||
|
|
e5629d9701 | ||
|
|
4ea38bba73 | ||
|
|
25cb1fb994 | ||
|
|
e5422db5c7 | ||
|
|
3a29f6aaff | ||
|
|
99d71b2b65 | ||
|
|
783f7ccb2c | ||
|
|
3f75fbf8fe | ||
|
|
4124f8ef70 | ||
|
|
b45ea0280b | ||
|
|
031ddfcb7b | ||
|
|
d686aa0d31 | ||
|
|
037366f93f | ||
|
|
5191ab6445 | ||
|
|
bfd3eb9dbc | ||
|
|
8fa9d9dcc9 | ||
|
|
21cd5c2f3d | ||
|
|
22948048b2 | ||
|
|
fa28d0e706 | ||
|
|
3ea544d89c | ||
|
|
9cef7a4600 | ||
|
|
c207b94e0c | ||
|
|
506c70884a | ||
|
|
f64f5495af | ||
|
|
3e9fc36729 | ||
|
|
8e798ebbdf | ||
|
|
0ad5ae0dbf | ||
|
|
0cf467e9e0 | ||
|
|
5ff0f7d0ca | ||
|
|
224ee0d4e5 | ||
|
|
ee26d1c578 | ||
|
|
18093d4c9a | ||
|
|
de1d14590d | ||
|
|
0058453fd9 | ||
|
|
7679f4d51a | ||
|
|
82a8c26bbf | ||
|
|
cb113991a3 | ||
|
|
0bf07a7f61 | ||
|
|
adb5b9c061 | ||
|
|
f0c967560a | ||
|
|
1cc63ade82 |
17
.air.toml
17
.air.toml
@@ -2,25 +2,12 @@ root = "."
|
|||||||
tmp_dir = ".air"
|
tmp_dir = ".air"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
pre_cmd = ["killall -9 gitea 2>/dev/null || true"] # kill off potential zombie processes from previous runs
|
|
||||||
cmd = "make --no-print-directory backend"
|
cmd = "make --no-print-directory backend"
|
||||||
bin = "gitea"
|
bin = "gitea"
|
||||||
delay = 2000
|
delay = 1000
|
||||||
include_ext = ["go", "tmpl"]
|
include_ext = ["go", "tmpl"]
|
||||||
include_file = ["main.go"]
|
include_file = ["main.go"]
|
||||||
include_dir = ["cmd", "models", "modules", "options", "routers", "services"]
|
include_dir = ["cmd", "models", "modules", "options", "routers", "services"]
|
||||||
exclude_dir = [
|
exclude_dir = ["modules/git/tests", "services/gitdiff/testdata", "modules/avatar/testdata", "models/fixtures", "models/migrations/fixtures", "modules/migration/file_format_testdata", "modules/avatar/identicon/testdata"]
|
||||||
"models/fixtures",
|
|
||||||
"models/migrations/fixtures",
|
|
||||||
"modules/avatar/identicon/testdata",
|
|
||||||
"modules/avatar/testdata",
|
|
||||||
"modules/git/tests",
|
|
||||||
"modules/migration/file_format_testdata",
|
|
||||||
"routers/private/tests",
|
|
||||||
"services/gitdiff/testdata",
|
|
||||||
]
|
|
||||||
exclude_regex = ["_test.go$", "_gen.go$"]
|
exclude_regex = ["_test.go$", "_gen.go$"]
|
||||||
stop_on_error = true
|
stop_on_error = true
|
||||||
|
|
||||||
[log]
|
|
||||||
main_only = true
|
|
||||||
|
|||||||
@@ -13,47 +13,46 @@ groups:
|
|||||||
-
|
-
|
||||||
name: BREAKING
|
name: BREAKING
|
||||||
labels:
|
labels:
|
||||||
- pr/breaking
|
- kind/breaking
|
||||||
-
|
-
|
||||||
name: SECURITY
|
name: SECURITY
|
||||||
labels:
|
labels:
|
||||||
- topic/security
|
- kind/security
|
||||||
-
|
-
|
||||||
name: FEATURES
|
name: FEATURES
|
||||||
labels:
|
labels:
|
||||||
- type/feature
|
- kind/feature
|
||||||
-
|
|
||||||
name: ENHANCEMENTS
|
|
||||||
labels:
|
|
||||||
- type/enhancement
|
|
||||||
-
|
|
||||||
name: PERFORMANCE
|
|
||||||
labels:
|
|
||||||
- performance/memory
|
|
||||||
- performance/speed
|
|
||||||
- performance/bigrepo
|
|
||||||
- performance/cpu
|
|
||||||
-
|
|
||||||
name: BUGFIXES
|
|
||||||
labels:
|
|
||||||
- type/bug
|
|
||||||
-
|
-
|
||||||
name: API
|
name: API
|
||||||
labels:
|
labels:
|
||||||
- modifies/api
|
- kind/api
|
||||||
|
-
|
||||||
|
name: ENHANCEMENTS
|
||||||
|
labels:
|
||||||
|
- kind/enhancement
|
||||||
|
- kind/refactor
|
||||||
|
- kind/ui
|
||||||
|
-
|
||||||
|
name: BUGFIXES
|
||||||
|
labels:
|
||||||
|
- kind/bug
|
||||||
-
|
-
|
||||||
name: TESTING
|
name: TESTING
|
||||||
labels:
|
labels:
|
||||||
- type/testing
|
- kind/testing
|
||||||
|
-
|
||||||
|
name: TRANSLATION
|
||||||
|
labels:
|
||||||
|
- kind/translation
|
||||||
-
|
-
|
||||||
name: BUILD
|
name: BUILD
|
||||||
labels:
|
labels:
|
||||||
- topic/build
|
- kind/build
|
||||||
- topic/code-linting
|
- kind/lint
|
||||||
-
|
-
|
||||||
name: DOCS
|
name: DOCS
|
||||||
labels:
|
labels:
|
||||||
- type/docs
|
- kind/docs
|
||||||
-
|
-
|
||||||
name: MISC
|
name: MISC
|
||||||
default: true
|
default: true
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Gitea DevContainer",
|
|
||||||
"image": "mcr.microsoft.com/devcontainers/go:1.24-bookworm",
|
|
||||||
"features": {
|
|
||||||
// installs nodejs into container
|
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
|
||||||
"version": "20"
|
|
||||||
},
|
|
||||||
"ghcr.io/devcontainers/features/git-lfs:1.2.2": {},
|
|
||||||
"ghcr.io/devcontainers-contrib/features/poetry:2": {},
|
|
||||||
"ghcr.io/devcontainers/features/python:1": {
|
|
||||||
"version": "3.12"
|
|
||||||
},
|
|
||||||
"ghcr.io/warrenbuckley/codespace-features/sqlite:1": {}
|
|
||||||
},
|
|
||||||
"customizations": {
|
|
||||||
"vscode": {
|
|
||||||
"settings": {},
|
|
||||||
// same extensions as Gitpod, should match /.gitpod.yml
|
|
||||||
"extensions": [
|
|
||||||
"editorconfig.editorconfig",
|
|
||||||
"dbaeumer.vscode-eslint",
|
|
||||||
"golang.go",
|
|
||||||
"stylelint.vscode-stylelint",
|
|
||||||
"DavidAnson.vscode-markdownlint",
|
|
||||||
"Vue.volar",
|
|
||||||
"ms-azuretools.vscode-docker",
|
|
||||||
"vitest.explorer",
|
|
||||||
"cweijan.vscode-database-client2",
|
|
||||||
"GitHub.vscode-pull-request-github",
|
|
||||||
"Azurite.azurite"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"portsAttributes": {
|
|
||||||
"3000": {
|
|
||||||
"label": "Gitea Web",
|
|
||||||
"onAutoForward": "notify"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"postCreateCommand": "make deps"
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,7 @@ _test
|
|||||||
|
|
||||||
# MS VSCode
|
# MS VSCode
|
||||||
.vscode
|
.vscode
|
||||||
__debug_bin*
|
__debug_bin
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
# Architecture specific extensions/prefixes
|
||||||
*.[568vq]
|
*.[568vq]
|
||||||
@@ -62,6 +62,7 @@ cpu.out
|
|||||||
/data
|
/data
|
||||||
/indexers
|
/indexers
|
||||||
/log
|
/log
|
||||||
|
/public/img/avatar
|
||||||
/tests/integration/gitea-integration-*
|
/tests/integration/gitea-integration-*
|
||||||
/tests/integration/indexers-*
|
/tests/integration/indexers-*
|
||||||
/tests/e2e/gitea-e2e-*
|
/tests/e2e/gitea-e2e-*
|
||||||
@@ -74,18 +75,27 @@ cpu.out
|
|||||||
/yarn.lock
|
/yarn.lock
|
||||||
/yarn-error.log
|
/yarn-error.log
|
||||||
/npm-debug.log*
|
/npm-debug.log*
|
||||||
/public/assets/js
|
/public/js
|
||||||
/public/assets/css
|
/public/css
|
||||||
/public/assets/fonts
|
/public/fonts
|
||||||
/public/assets/img/avatar
|
/public/img/webpack
|
||||||
/vendor
|
/vendor
|
||||||
|
/web_src/fomantic/node_modules
|
||||||
|
/web_src/fomantic/build/*
|
||||||
|
!/web_src/fomantic/build/semantic.js
|
||||||
|
!/web_src/fomantic/build/semantic.css
|
||||||
|
!/web_src/fomantic/build/themes
|
||||||
|
/web_src/fomantic/build/themes/*
|
||||||
|
!/web_src/fomantic/build/themes/default
|
||||||
|
/web_src/fomantic/build/themes/default/assets/*
|
||||||
|
!/web_src/fomantic/build/themes/default/assets/fonts
|
||||||
|
/web_src/fomantic/build/themes/default/assets/fonts/*
|
||||||
|
!/web_src/fomantic/build/themes/default/assets/fonts/icons.woff2
|
||||||
|
!/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
|
||||||
/VERSION
|
/VERSION
|
||||||
/.air
|
/.air
|
||||||
/.go-licenses
|
/.go-licenses
|
||||||
|
|
||||||
# Files and folders that were previously generated
|
|
||||||
/public/assets/img/webpack
|
|
||||||
|
|
||||||
# Snapcraft
|
# Snapcraft
|
||||||
snap/.snapcraft/
|
snap/.snapcraft/
|
||||||
parts/
|
parts/
|
||||||
|
|||||||
426
.drone.yml
Normal file
426
.drone.yml
Normal file
@@ -0,0 +1,426 @@
|
|||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: release-version
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
workspace:
|
||||||
|
base: /source
|
||||||
|
path: /
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- tag
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
temp: {}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: fetch-tags
|
||||||
|
image: docker:git
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- git fetch --tags --force
|
||||||
|
|
||||||
|
- name: deps-frontend
|
||||||
|
image: node:20
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- make deps-frontend
|
||||||
|
|
||||||
|
- name: deps-backend
|
||||||
|
image: gitea/test_env:linux-1.20-amd64
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- make deps-backend
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /go
|
||||||
|
|
||||||
|
- name: static
|
||||||
|
image: techknowlogick/xgo:go-1.20.x
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
# Upgrade to node 20 once https://github.com/techknowlogick/xgo/issues/163 is resolved
|
||||||
|
- curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get -qqy install nodejs
|
||||||
|
- export PATH=$PATH:$GOPATH/bin
|
||||||
|
- make release
|
||||||
|
environment:
|
||||||
|
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||||
|
TAGS: bindata sqlite sqlite_unlock_notify
|
||||||
|
DEBIAN_FRONTEND: noninteractive
|
||||||
|
depends_on: [fetch-tags]
|
||||||
|
volumes:
|
||||||
|
- name: deps
|
||||||
|
path: /go
|
||||||
|
|
||||||
|
- name: gpg-sign
|
||||||
|
image: plugins/gpgsign:1
|
||||||
|
pull: always
|
||||||
|
settings:
|
||||||
|
detach_sign: true
|
||||||
|
excludes:
|
||||||
|
- "dist/release/*.sha256"
|
||||||
|
files:
|
||||||
|
- "dist/release/*"
|
||||||
|
environment:
|
||||||
|
GPGSIGN_KEY:
|
||||||
|
from_secret: gpgsign_key
|
||||||
|
GPGSIGN_PASSPHRASE:
|
||||||
|
from_secret: gpgsign_passphrase
|
||||||
|
depends_on: [static]
|
||||||
|
|
||||||
|
- name: release-tag
|
||||||
|
image: woodpeckerci/plugin-s3:latest
|
||||||
|
pull: always
|
||||||
|
settings:
|
||||||
|
acl:
|
||||||
|
from_secret: aws_s3_acl
|
||||||
|
region:
|
||||||
|
from_secret: aws_s3_region
|
||||||
|
bucket:
|
||||||
|
from_secret: aws_s3_bucket
|
||||||
|
endpoint:
|
||||||
|
from_secret: aws_s3_endpoint
|
||||||
|
path_style:
|
||||||
|
from_secret: aws_s3_path_style
|
||||||
|
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
|
||||||
|
depends_on: [gpg-sign]
|
||||||
|
|
||||||
|
- name: github
|
||||||
|
image: plugins/github-release:latest
|
||||||
|
pull: always
|
||||||
|
settings:
|
||||||
|
files:
|
||||||
|
- "dist/release/*"
|
||||||
|
file_exists: overwrite
|
||||||
|
environment:
|
||||||
|
GITHUB_TOKEN:
|
||||||
|
from_secret: github_token
|
||||||
|
depends_on: [gpg-sign]
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: docker-linux-amd64-release-version
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
ref:
|
||||||
|
include:
|
||||||
|
- "refs/tags/**"
|
||||||
|
exclude:
|
||||||
|
- "refs/tags/**-rc*"
|
||||||
|
paths:
|
||||||
|
exclude:
|
||||||
|
- "docs/**"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: fetch-tags
|
||||||
|
image: docker:git
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- git fetch --tags --force
|
||||||
|
|
||||||
|
- name: publish
|
||||||
|
image: plugins/docker:latest
|
||||||
|
pull: always
|
||||||
|
settings:
|
||||||
|
auto_tag: true
|
||||||
|
auto_tag_suffix: linux-amd64
|
||||||
|
repo: gitea/gitea
|
||||||
|
build_args:
|
||||||
|
- GOPROXY=https://goproxy.io
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
environment:
|
||||||
|
PLUGIN_MIRROR:
|
||||||
|
from_secret: plugin_mirror
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
- name: publish-rootless
|
||||||
|
image: plugins/docker:latest
|
||||||
|
settings:
|
||||||
|
dockerfile: Dockerfile.rootless
|
||||||
|
auto_tag: true
|
||||||
|
auto_tag_suffix: linux-amd64-rootless
|
||||||
|
repo: gitea/gitea
|
||||||
|
build_args:
|
||||||
|
- GOPROXY=https://goproxy.io
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
environment:
|
||||||
|
PLUGIN_MIRROR:
|
||||||
|
from_secret: plugin_mirror
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
---
|
||||||
|
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: docker-linux-amd64-release-candidate-version
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
ref:
|
||||||
|
- "refs/tags/**-rc*"
|
||||||
|
paths:
|
||||||
|
exclude:
|
||||||
|
- "docs/**"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: fetch-tags
|
||||||
|
image: docker:git
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- git fetch --tags --force
|
||||||
|
|
||||||
|
- name: publish
|
||||||
|
image: plugins/docker:latest
|
||||||
|
pull: always
|
||||||
|
settings:
|
||||||
|
tags: ${DRONE_TAG##v}-linux-amd64
|
||||||
|
repo: gitea/gitea
|
||||||
|
build_args:
|
||||||
|
- GOPROXY=https://goproxy.io
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
environment:
|
||||||
|
PLUGIN_MIRROR:
|
||||||
|
from_secret: plugin_mirror
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
- name: publish-rootless
|
||||||
|
image: plugins/docker:latest
|
||||||
|
settings:
|
||||||
|
dockerfile: Dockerfile.rootless
|
||||||
|
tags: ${DRONE_TAG##v}-linux-amd64-rootless
|
||||||
|
repo: gitea/gitea
|
||||||
|
build_args:
|
||||||
|
- GOPROXY=https://goproxy.io
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
environment:
|
||||||
|
PLUGIN_MIRROR:
|
||||||
|
from_secret: plugin_mirror
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: docker-linux-arm64-release-version
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: arm64
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
ref:
|
||||||
|
include:
|
||||||
|
- "refs/tags/**"
|
||||||
|
exclude:
|
||||||
|
- "refs/tags/**-rc*"
|
||||||
|
paths:
|
||||||
|
exclude:
|
||||||
|
- "docs/**"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: fetch-tags
|
||||||
|
image: docker:git
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- git fetch --tags --force
|
||||||
|
|
||||||
|
- name: publish
|
||||||
|
image: plugins/docker:latest
|
||||||
|
pull: always
|
||||||
|
settings:
|
||||||
|
auto_tag: true
|
||||||
|
auto_tag_suffix: linux-arm64
|
||||||
|
repo: gitea/gitea
|
||||||
|
build_args:
|
||||||
|
- GOPROXY=https://goproxy.io
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
environment:
|
||||||
|
PLUGIN_MIRROR:
|
||||||
|
from_secret: plugin_mirror
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
- name: publish-rootless
|
||||||
|
image: plugins/docker:latest
|
||||||
|
settings:
|
||||||
|
dockerfile: Dockerfile.rootless
|
||||||
|
auto_tag: true
|
||||||
|
auto_tag_suffix: linux-arm64-rootless
|
||||||
|
repo: gitea/gitea
|
||||||
|
build_args:
|
||||||
|
- GOPROXY=https://goproxy.io
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
environment:
|
||||||
|
PLUGIN_MIRROR:
|
||||||
|
from_secret: plugin_mirror
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: docker-linux-arm64-release-candidate-version
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: arm64
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
ref:
|
||||||
|
- "refs/tags/**-rc*"
|
||||||
|
paths:
|
||||||
|
exclude:
|
||||||
|
- "docs/**"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: fetch-tags
|
||||||
|
image: docker:git
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- git fetch --tags --force
|
||||||
|
|
||||||
|
- name: publish
|
||||||
|
image: plugins/docker:latest
|
||||||
|
pull: always
|
||||||
|
settings:
|
||||||
|
tags: ${DRONE_TAG##v}-linux-arm64
|
||||||
|
repo: gitea/gitea
|
||||||
|
build_args:
|
||||||
|
- GOPROXY=https://goproxy.io
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
environment:
|
||||||
|
PLUGIN_MIRROR:
|
||||||
|
from_secret: plugin_mirror
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
- name: publish-rootless
|
||||||
|
image: plugins/docker:latest
|
||||||
|
settings:
|
||||||
|
dockerfile: Dockerfile.rootless
|
||||||
|
tags: ${DRONE_TAG##v}-linux-arm64-rootless
|
||||||
|
repo: gitea/gitea
|
||||||
|
build_args:
|
||||||
|
- GOPROXY=https://goproxy.io
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
environment:
|
||||||
|
PLUGIN_MIRROR:
|
||||||
|
from_secret: plugin_mirror
|
||||||
|
DOCKER_BUILDKIT: 1
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: docker-manifest-version
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: manifest-rootless
|
||||||
|
image: plugins/manifest
|
||||||
|
pull: always
|
||||||
|
settings:
|
||||||
|
auto_tag: true
|
||||||
|
ignore_missing: true
|
||||||
|
spec: docker/manifest.rootless.tmpl
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
|
||||||
|
- name: manifest
|
||||||
|
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/tags/**"
|
||||||
|
paths:
|
||||||
|
exclude:
|
||||||
|
- "docs/**"
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- docker-linux-amd64-release-version
|
||||||
|
- docker-linux-amd64-release-candidate-version
|
||||||
|
- docker-linux-arm64-release-version
|
||||||
|
- docker-linux-arm64-release-candidate-version
|
||||||
@@ -12,15 +12,11 @@ insert_final_newline = true
|
|||||||
[*.{go,tmpl,html}]
|
[*.{go,tmpl,html}]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
|
||||||
[go.*]
|
|
||||||
indent_style = tab
|
|
||||||
|
|
||||||
[templates/custom/*.tmpl]
|
[templates/custom/*.tmpl]
|
||||||
insert_final_newline = false
|
insert_final_newline = false
|
||||||
|
|
||||||
[templates/swagger/v1_json.tmpl]
|
[templates/swagger/v1_json.tmpl]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
insert_final_newline = false
|
|
||||||
|
|
||||||
[templates/user/auth/oidc_wellknown.tmpl]
|
[templates/user/auth/oidc_wellknown.tmpl]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
|||||||
1004
.eslintrc.cjs
1004
.eslintrc.cjs
File diff suppressed because it is too large
Load Diff
752
.eslintrc.yaml
Normal file
752
.eslintrc.yaml
Normal file
@@ -0,0 +1,752 @@
|
|||||||
|
root: true
|
||||||
|
reportUnusedDisableDirectives: true
|
||||||
|
|
||||||
|
ignorePatterns:
|
||||||
|
- /web_src/js/vendor
|
||||||
|
|
||||||
|
parserOptions:
|
||||||
|
sourceType: module
|
||||||
|
ecmaVersion: latest
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- "@eslint-community/eslint-plugin-eslint-comments"
|
||||||
|
- eslint-plugin-array-func
|
||||||
|
- eslint-plugin-custom-elements
|
||||||
|
- eslint-plugin-import
|
||||||
|
- eslint-plugin-jquery
|
||||||
|
- eslint-plugin-no-jquery
|
||||||
|
- eslint-plugin-no-use-extend-native
|
||||||
|
- eslint-plugin-regexp
|
||||||
|
- eslint-plugin-sonarjs
|
||||||
|
- eslint-plugin-unicorn
|
||||||
|
- eslint-plugin-wc
|
||||||
|
|
||||||
|
env:
|
||||||
|
es2022: true
|
||||||
|
node: true
|
||||||
|
|
||||||
|
globals:
|
||||||
|
__webpack_public_path__: true
|
||||||
|
|
||||||
|
overrides:
|
||||||
|
- files: ["web_src/**/*", "docs/**/*"]
|
||||||
|
env:
|
||||||
|
browser: true
|
||||||
|
node: false
|
||||||
|
- files: ["web_src/**/*worker.*"]
|
||||||
|
env:
|
||||||
|
worker: true
|
||||||
|
rules:
|
||||||
|
no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, status, statusbar, stop, toolbar, top]
|
||||||
|
- files: ["build/generate-images.js"]
|
||||||
|
rules:
|
||||||
|
import/no-unresolved: [0]
|
||||||
|
import/no-extraneous-dependencies: [0]
|
||||||
|
- files: ["*.config.*"]
|
||||||
|
rules:
|
||||||
|
import/no-unused-modules: [0]
|
||||||
|
|
||||||
|
rules:
|
||||||
|
"@eslint-community/eslint-comments/disable-enable-pair": [2]
|
||||||
|
"@eslint-community/eslint-comments/no-aggregating-enable": [2]
|
||||||
|
"@eslint-community/eslint-comments/no-duplicate-disable": [2]
|
||||||
|
"@eslint-community/eslint-comments/no-restricted-disable": [0]
|
||||||
|
"@eslint-community/eslint-comments/no-unlimited-disable": [2]
|
||||||
|
"@eslint-community/eslint-comments/no-unused-disable": [2]
|
||||||
|
"@eslint-community/eslint-comments/no-unused-enable": [2]
|
||||||
|
"@eslint-community/eslint-comments/no-use": [0]
|
||||||
|
"@eslint-community/eslint-comments/require-description": [0]
|
||||||
|
accessor-pairs: [2]
|
||||||
|
array-bracket-newline: [0]
|
||||||
|
array-bracket-spacing: [2, never]
|
||||||
|
array-callback-return: [2, {checkForEach: true}]
|
||||||
|
array-element-newline: [0]
|
||||||
|
array-func/avoid-reverse: [2]
|
||||||
|
array-func/from-map: [2]
|
||||||
|
array-func/no-unnecessary-this-arg: [2]
|
||||||
|
array-func/prefer-array-from: [2]
|
||||||
|
array-func/prefer-flat-map: [0] # handled by unicorn/prefer-array-flat-map
|
||||||
|
array-func/prefer-flat: [0] # handled by unicorn/prefer-array-flat
|
||||||
|
arrow-body-style: [0]
|
||||||
|
arrow-parens: [2, always]
|
||||||
|
arrow-spacing: [2, {before: true, after: true}]
|
||||||
|
block-scoped-var: [2]
|
||||||
|
brace-style: [2, 1tbs, {allowSingleLine: true}]
|
||||||
|
camelcase: [0]
|
||||||
|
capitalized-comments: [0]
|
||||||
|
class-methods-use-this: [0]
|
||||||
|
comma-dangle: [2, only-multiline]
|
||||||
|
comma-spacing: [2, {before: false, after: true}]
|
||||||
|
comma-style: [2, last]
|
||||||
|
complexity: [0]
|
||||||
|
computed-property-spacing: [2, never]
|
||||||
|
consistent-return: [0]
|
||||||
|
consistent-this: [0]
|
||||||
|
constructor-super: [2]
|
||||||
|
curly: [0]
|
||||||
|
custom-elements/expose-class-on-global: [0]
|
||||||
|
custom-elements/extends-correct-class: [2]
|
||||||
|
custom-elements/file-name-matches-element: [2]
|
||||||
|
custom-elements/no-constructor: [2]
|
||||||
|
custom-elements/no-customized-built-in-elements: [2]
|
||||||
|
custom-elements/no-dom-traversal-in-attributechangedcallback: [2]
|
||||||
|
custom-elements/no-dom-traversal-in-connectedcallback: [2]
|
||||||
|
custom-elements/no-exports-with-element: [2]
|
||||||
|
custom-elements/no-method-prefixed-with-on: [2]
|
||||||
|
custom-elements/no-unchecked-define: [0]
|
||||||
|
custom-elements/one-element-per-file: [0]
|
||||||
|
custom-elements/tag-name-matches-class: [2]
|
||||||
|
custom-elements/valid-tag-name: [2]
|
||||||
|
default-case-last: [2]
|
||||||
|
default-case: [0]
|
||||||
|
default-param-last: [0]
|
||||||
|
dot-location: [2, property]
|
||||||
|
dot-notation: [0]
|
||||||
|
eol-last: [2]
|
||||||
|
eqeqeq: [2]
|
||||||
|
for-direction: [2]
|
||||||
|
func-call-spacing: [2, never]
|
||||||
|
func-name-matching: [2]
|
||||||
|
func-names: [0]
|
||||||
|
func-style: [0]
|
||||||
|
function-call-argument-newline: [0]
|
||||||
|
function-paren-newline: [0]
|
||||||
|
generator-star-spacing: [0]
|
||||||
|
getter-return: [2]
|
||||||
|
grouped-accessor-pairs: [2]
|
||||||
|
guard-for-in: [0]
|
||||||
|
id-blacklist: [0]
|
||||||
|
id-length: [0]
|
||||||
|
id-match: [0]
|
||||||
|
implicit-arrow-linebreak: [0]
|
||||||
|
import/consistent-type-specifier-style: [0]
|
||||||
|
import/default: [0]
|
||||||
|
import/dynamic-import-chunkname: [0]
|
||||||
|
import/export: [2]
|
||||||
|
import/exports-last: [0]
|
||||||
|
import/extensions: [2, always, {ignorePackages: true}]
|
||||||
|
import/first: [2]
|
||||||
|
import/group-exports: [0]
|
||||||
|
import/max-dependencies: [0]
|
||||||
|
import/named: [2]
|
||||||
|
import/namespace: [0]
|
||||||
|
import/newline-after-import: [0]
|
||||||
|
import/no-absolute-path: [0]
|
||||||
|
import/no-amd: [2]
|
||||||
|
import/no-anonymous-default-export: [0]
|
||||||
|
import/no-commonjs: [2]
|
||||||
|
import/no-cycle: [2, {ignoreExternal: true, maxDepth: 1}]
|
||||||
|
import/no-default-export: [0]
|
||||||
|
import/no-deprecated: [0]
|
||||||
|
import/no-dynamic-require: [0]
|
||||||
|
import/no-empty-named-blocks: [2]
|
||||||
|
import/no-extraneous-dependencies: [2]
|
||||||
|
import/no-import-module-exports: [0]
|
||||||
|
import/no-internal-modules: [0]
|
||||||
|
import/no-mutable-exports: [0]
|
||||||
|
import/no-named-as-default-member: [0]
|
||||||
|
import/no-named-as-default: [2]
|
||||||
|
import/no-named-default: [0]
|
||||||
|
import/no-named-export: [0]
|
||||||
|
import/no-namespace: [0]
|
||||||
|
import/no-nodejs-modules: [0]
|
||||||
|
import/no-relative-packages: [0]
|
||||||
|
import/no-relative-parent-imports: [0]
|
||||||
|
import/no-restricted-paths: [0]
|
||||||
|
import/no-self-import: [2]
|
||||||
|
import/no-unassigned-import: [0]
|
||||||
|
import/no-unresolved: [2, {commonjs: true, ignore: ["\\?.+$"]}]
|
||||||
|
import/no-unused-modules: [2, {unusedExports: true}]
|
||||||
|
import/no-useless-path-segments: [2, {commonjs: true}]
|
||||||
|
import/no-webpack-loader-syntax: [2]
|
||||||
|
import/order: [0]
|
||||||
|
import/prefer-default-export: [0]
|
||||||
|
import/unambiguous: [0]
|
||||||
|
indent: [2, 2, {SwitchCase: 1}]
|
||||||
|
init-declarations: [0]
|
||||||
|
jquery/no-ajax-events: [2]
|
||||||
|
jquery/no-ajax: [0]
|
||||||
|
jquery/no-animate: [2]
|
||||||
|
jquery/no-attr: [0]
|
||||||
|
jquery/no-bind: [2]
|
||||||
|
jquery/no-class: [0]
|
||||||
|
jquery/no-clone: [2]
|
||||||
|
jquery/no-closest: [0]
|
||||||
|
jquery/no-css: [0]
|
||||||
|
jquery/no-data: [0]
|
||||||
|
jquery/no-deferred: [2]
|
||||||
|
jquery/no-delegate: [2]
|
||||||
|
jquery/no-each: [0]
|
||||||
|
jquery/no-extend: [2]
|
||||||
|
jquery/no-fade: [0]
|
||||||
|
jquery/no-filter: [0]
|
||||||
|
jquery/no-find: [0]
|
||||||
|
jquery/no-global-eval: [2]
|
||||||
|
jquery/no-grep: [2]
|
||||||
|
jquery/no-has: [2]
|
||||||
|
jquery/no-hide: [2]
|
||||||
|
jquery/no-html: [0]
|
||||||
|
jquery/no-in-array: [2]
|
||||||
|
jquery/no-is-array: [2]
|
||||||
|
jquery/no-is-function: [2]
|
||||||
|
jquery/no-is: [0]
|
||||||
|
jquery/no-load: [2]
|
||||||
|
jquery/no-map: [0]
|
||||||
|
jquery/no-merge: [2]
|
||||||
|
jquery/no-param: [2]
|
||||||
|
jquery/no-parent: [0]
|
||||||
|
jquery/no-parents: [0]
|
||||||
|
jquery/no-parse-html: [2]
|
||||||
|
jquery/no-prop: [0]
|
||||||
|
jquery/no-proxy: [2]
|
||||||
|
jquery/no-ready: [2]
|
||||||
|
jquery/no-serialize: [2]
|
||||||
|
jquery/no-show: [2]
|
||||||
|
jquery/no-size: [2]
|
||||||
|
jquery/no-sizzle: [0]
|
||||||
|
jquery/no-slide: [0]
|
||||||
|
jquery/no-submit: [0]
|
||||||
|
jquery/no-text: [0]
|
||||||
|
jquery/no-toggle: [2]
|
||||||
|
jquery/no-trigger: [0]
|
||||||
|
jquery/no-trim: [2]
|
||||||
|
jquery/no-val: [0]
|
||||||
|
jquery/no-when: [2]
|
||||||
|
jquery/no-wrap: [2]
|
||||||
|
key-spacing: [2]
|
||||||
|
keyword-spacing: [2]
|
||||||
|
line-comment-position: [0]
|
||||||
|
linebreak-style: [2, unix]
|
||||||
|
lines-around-comment: [0]
|
||||||
|
lines-between-class-members: [0]
|
||||||
|
logical-assignment-operators: [0]
|
||||||
|
max-classes-per-file: [0]
|
||||||
|
max-depth: [0]
|
||||||
|
max-len: [0]
|
||||||
|
max-lines-per-function: [0]
|
||||||
|
max-lines: [0]
|
||||||
|
max-nested-callbacks: [0]
|
||||||
|
max-params: [0]
|
||||||
|
max-statements-per-line: [0]
|
||||||
|
max-statements: [0]
|
||||||
|
multiline-comment-style: [2, separate-lines]
|
||||||
|
multiline-ternary: [0]
|
||||||
|
new-cap: [0]
|
||||||
|
new-parens: [2]
|
||||||
|
newline-per-chained-call: [0]
|
||||||
|
no-alert: [0]
|
||||||
|
no-array-constructor: [2]
|
||||||
|
no-async-promise-executor: [0]
|
||||||
|
no-await-in-loop: [0]
|
||||||
|
no-bitwise: [0]
|
||||||
|
no-buffer-constructor: [0]
|
||||||
|
no-caller: [2]
|
||||||
|
no-case-declarations: [2]
|
||||||
|
no-class-assign: [2]
|
||||||
|
no-compare-neg-zero: [2]
|
||||||
|
no-cond-assign: [2, except-parens]
|
||||||
|
no-confusing-arrow: [0]
|
||||||
|
no-console: [1, {allow: [debug, info, warn, error]}]
|
||||||
|
no-const-assign: [2]
|
||||||
|
no-constant-binary-expression: [2]
|
||||||
|
no-constant-condition: [0]
|
||||||
|
no-constructor-return: [2]
|
||||||
|
no-continue: [0]
|
||||||
|
no-control-regex: [0]
|
||||||
|
no-debugger: [1]
|
||||||
|
no-delete-var: [2]
|
||||||
|
no-div-regex: [0]
|
||||||
|
no-dupe-args: [2]
|
||||||
|
no-dupe-class-members: [2]
|
||||||
|
no-dupe-else-if: [2]
|
||||||
|
no-dupe-keys: [2]
|
||||||
|
no-duplicate-case: [2]
|
||||||
|
no-duplicate-imports: [2]
|
||||||
|
no-else-return: [2]
|
||||||
|
no-empty-character-class: [2]
|
||||||
|
no-empty-function: [0]
|
||||||
|
no-empty-pattern: [2]
|
||||||
|
no-empty-static-block: [2]
|
||||||
|
no-empty: [2, {allowEmptyCatch: true}]
|
||||||
|
no-eq-null: [2]
|
||||||
|
no-eval: [2]
|
||||||
|
no-ex-assign: [2]
|
||||||
|
no-extend-native: [2]
|
||||||
|
no-extra-bind: [2]
|
||||||
|
no-extra-boolean-cast: [2]
|
||||||
|
no-extra-label: [0]
|
||||||
|
no-extra-parens: [0]
|
||||||
|
no-extra-semi: [2]
|
||||||
|
no-fallthrough: [2]
|
||||||
|
no-floating-decimal: [0]
|
||||||
|
no-func-assign: [2]
|
||||||
|
no-global-assign: [2]
|
||||||
|
no-implicit-coercion: [2]
|
||||||
|
no-implicit-globals: [0]
|
||||||
|
no-implied-eval: [2]
|
||||||
|
no-import-assign: [2]
|
||||||
|
no-inline-comments: [0]
|
||||||
|
no-inner-declarations: [2]
|
||||||
|
no-invalid-regexp: [2]
|
||||||
|
no-invalid-this: [0]
|
||||||
|
no-irregular-whitespace: [2]
|
||||||
|
no-iterator: [2]
|
||||||
|
no-jquery/no-ajax-events: [2]
|
||||||
|
no-jquery/no-ajax: [0]
|
||||||
|
no-jquery/no-and-self: [2]
|
||||||
|
no-jquery/no-animate-toggle: [2]
|
||||||
|
no-jquery/no-animate: [2]
|
||||||
|
no-jquery/no-append-html: [0]
|
||||||
|
no-jquery/no-attr: [0]
|
||||||
|
no-jquery/no-bind: [2]
|
||||||
|
no-jquery/no-box-model: [2]
|
||||||
|
no-jquery/no-browser: [2]
|
||||||
|
no-jquery/no-camel-case: [2]
|
||||||
|
no-jquery/no-class-state: [0]
|
||||||
|
no-jquery/no-class: [0]
|
||||||
|
no-jquery/no-clone: [2]
|
||||||
|
no-jquery/no-closest: [0]
|
||||||
|
no-jquery/no-constructor-attributes: [2]
|
||||||
|
no-jquery/no-contains: [2]
|
||||||
|
no-jquery/no-context-prop: [2]
|
||||||
|
no-jquery/no-css: [0]
|
||||||
|
no-jquery/no-data: [0]
|
||||||
|
no-jquery/no-deferred: [2]
|
||||||
|
no-jquery/no-delegate: [2]
|
||||||
|
no-jquery/no-each-collection: [0]
|
||||||
|
no-jquery/no-each-util: [0]
|
||||||
|
no-jquery/no-each: [0]
|
||||||
|
no-jquery/no-error-shorthand: [2]
|
||||||
|
no-jquery/no-error: [2]
|
||||||
|
no-jquery/no-escape-selector: [2]
|
||||||
|
no-jquery/no-event-shorthand: [2]
|
||||||
|
no-jquery/no-extend: [2]
|
||||||
|
no-jquery/no-fade: [2]
|
||||||
|
no-jquery/no-filter: [0]
|
||||||
|
no-jquery/no-find-collection: [0]
|
||||||
|
no-jquery/no-find-util: [2]
|
||||||
|
no-jquery/no-find: [0]
|
||||||
|
no-jquery/no-fx-interval: [2]
|
||||||
|
no-jquery/no-global-eval: [2]
|
||||||
|
no-jquery/no-global-selector: [0]
|
||||||
|
no-jquery/no-grep: [2]
|
||||||
|
no-jquery/no-has: [2]
|
||||||
|
no-jquery/no-hold-ready: [2]
|
||||||
|
no-jquery/no-html: [0]
|
||||||
|
no-jquery/no-in-array: [2]
|
||||||
|
no-jquery/no-is-array: [2]
|
||||||
|
no-jquery/no-is-empty-object: [2]
|
||||||
|
no-jquery/no-is-function: [2]
|
||||||
|
no-jquery/no-is-numeric: [2]
|
||||||
|
no-jquery/no-is-plain-object: [2]
|
||||||
|
no-jquery/no-is-window: [2]
|
||||||
|
no-jquery/no-is: [0]
|
||||||
|
no-jquery/no-jquery-constructor: [0]
|
||||||
|
no-jquery/no-live: [2]
|
||||||
|
no-jquery/no-load-shorthand: [2]
|
||||||
|
no-jquery/no-load: [2]
|
||||||
|
no-jquery/no-map-collection: [0]
|
||||||
|
no-jquery/no-map-util: [2]
|
||||||
|
no-jquery/no-map: [0]
|
||||||
|
no-jquery/no-merge: [2]
|
||||||
|
no-jquery/no-node-name: [2]
|
||||||
|
no-jquery/no-noop: [2]
|
||||||
|
no-jquery/no-now: [2]
|
||||||
|
no-jquery/no-on-ready: [2]
|
||||||
|
no-jquery/no-other-methods: [0]
|
||||||
|
no-jquery/no-other-utils: [2]
|
||||||
|
no-jquery/no-param: [2]
|
||||||
|
no-jquery/no-parent: [0]
|
||||||
|
no-jquery/no-parents: [0]
|
||||||
|
no-jquery/no-parse-html-literal: [0]
|
||||||
|
no-jquery/no-parse-html: [2]
|
||||||
|
no-jquery/no-parse-json: [2]
|
||||||
|
no-jquery/no-parse-xml: [2]
|
||||||
|
no-jquery/no-prop: [0]
|
||||||
|
no-jquery/no-proxy: [2]
|
||||||
|
no-jquery/no-ready-shorthand: [2]
|
||||||
|
no-jquery/no-ready: [2]
|
||||||
|
no-jquery/no-selector-prop: [2]
|
||||||
|
no-jquery/no-serialize: [2]
|
||||||
|
no-jquery/no-size: [2]
|
||||||
|
no-jquery/no-sizzle: [0]
|
||||||
|
no-jquery/no-slide: [2]
|
||||||
|
no-jquery/no-sub: [2]
|
||||||
|
no-jquery/no-support: [2]
|
||||||
|
no-jquery/no-text: [0]
|
||||||
|
no-jquery/no-trigger: [0]
|
||||||
|
no-jquery/no-trim: [2]
|
||||||
|
no-jquery/no-type: [2]
|
||||||
|
no-jquery/no-unique: [2]
|
||||||
|
no-jquery/no-unload-shorthand: [2]
|
||||||
|
no-jquery/no-val: [0]
|
||||||
|
no-jquery/no-visibility: [2]
|
||||||
|
no-jquery/no-when: [2]
|
||||||
|
no-jquery/no-wrap: [2]
|
||||||
|
no-jquery/variable-pattern: [0]
|
||||||
|
no-label-var: [2]
|
||||||
|
no-labels: [0] # handled by no-restricted-syntax
|
||||||
|
no-lone-blocks: [2]
|
||||||
|
no-lonely-if: [0]
|
||||||
|
no-loop-func: [0]
|
||||||
|
no-loss-of-precision: [2]
|
||||||
|
no-magic-numbers: [0]
|
||||||
|
no-misleading-character-class: [2]
|
||||||
|
no-mixed-operators: [0]
|
||||||
|
no-mixed-spaces-and-tabs: [2]
|
||||||
|
no-multi-assign: [0]
|
||||||
|
no-multi-spaces: [2, {ignoreEOLComments: true, exceptions: {Property: true}}]
|
||||||
|
no-multi-str: [2]
|
||||||
|
no-negated-condition: [0]
|
||||||
|
no-nested-ternary: [0]
|
||||||
|
no-new-func: [2]
|
||||||
|
no-new-native-nonconstructor: [2]
|
||||||
|
no-new-object: [2]
|
||||||
|
no-new-symbol: [2]
|
||||||
|
no-new-wrappers: [2]
|
||||||
|
no-new: [0]
|
||||||
|
no-nonoctal-decimal-escape: [2]
|
||||||
|
no-obj-calls: [2]
|
||||||
|
no-octal-escape: [2]
|
||||||
|
no-octal: [2]
|
||||||
|
no-param-reassign: [0]
|
||||||
|
no-plusplus: [0]
|
||||||
|
no-promise-executor-return: [0]
|
||||||
|
no-proto: [2]
|
||||||
|
no-prototype-builtins: [2]
|
||||||
|
no-redeclare: [2]
|
||||||
|
no-regex-spaces: [2]
|
||||||
|
no-restricted-exports: [0]
|
||||||
|
no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, location, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, self, status, statusbar, stop, toolbar, top, __dirname, __filename]
|
||||||
|
no-restricted-imports: [0]
|
||||||
|
no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement]
|
||||||
|
no-return-assign: [0]
|
||||||
|
no-return-await: [0]
|
||||||
|
no-script-url: [2]
|
||||||
|
no-self-assign: [2, {props: true}]
|
||||||
|
no-self-compare: [2]
|
||||||
|
no-sequences: [2]
|
||||||
|
no-setter-return: [2]
|
||||||
|
no-shadow-restricted-names: [2]
|
||||||
|
no-shadow: [0]
|
||||||
|
no-sparse-arrays: [2]
|
||||||
|
no-tabs: [2]
|
||||||
|
no-template-curly-in-string: [2]
|
||||||
|
no-ternary: [0]
|
||||||
|
no-this-before-super: [2]
|
||||||
|
no-throw-literal: [2]
|
||||||
|
no-trailing-spaces: [2]
|
||||||
|
no-undef-init: [2]
|
||||||
|
no-undef: [2, {typeof: true}]
|
||||||
|
no-undefined: [0]
|
||||||
|
no-underscore-dangle: [0]
|
||||||
|
no-unexpected-multiline: [2]
|
||||||
|
no-unmodified-loop-condition: [2]
|
||||||
|
no-unneeded-ternary: [0]
|
||||||
|
no-unreachable-loop: [2]
|
||||||
|
no-unreachable: [2]
|
||||||
|
no-unsafe-finally: [2]
|
||||||
|
no-unsafe-negation: [2]
|
||||||
|
no-unused-expressions: [2]
|
||||||
|
no-unused-labels: [2]
|
||||||
|
no-unused-private-class-members: [2]
|
||||||
|
no-unused-vars: [2, {args: all, argsIgnorePattern: ^_, varsIgnorePattern: ^_, caughtErrorsIgnorePattern: ^_, destructuredArrayIgnorePattern: ^_, ignoreRestSiblings: false}]
|
||||||
|
no-use-before-define: [2, {functions: false, classes: true, variables: true, allowNamedExports: true}]
|
||||||
|
no-use-extend-native/no-use-extend-native: [2]
|
||||||
|
no-useless-backreference: [2]
|
||||||
|
no-useless-call: [2]
|
||||||
|
no-useless-catch: [2]
|
||||||
|
no-useless-computed-key: [2]
|
||||||
|
no-useless-concat: [2]
|
||||||
|
no-useless-constructor: [2]
|
||||||
|
no-useless-escape: [2]
|
||||||
|
no-useless-rename: [2]
|
||||||
|
no-useless-return: [2]
|
||||||
|
no-var: [2]
|
||||||
|
no-void: [2]
|
||||||
|
no-warning-comments: [0]
|
||||||
|
no-whitespace-before-property: [2]
|
||||||
|
no-with: [0] # handled by no-restricted-syntax
|
||||||
|
nonblock-statement-body-position: [2]
|
||||||
|
object-curly-newline: [0]
|
||||||
|
object-curly-spacing: [2, never]
|
||||||
|
object-shorthand: [2, always]
|
||||||
|
one-var-declaration-per-line: [0]
|
||||||
|
one-var: [0]
|
||||||
|
operator-assignment: [2, always]
|
||||||
|
operator-linebreak: [2, after]
|
||||||
|
padded-blocks: [2, never]
|
||||||
|
padding-line-between-statements: [0]
|
||||||
|
prefer-arrow-callback: [2, {allowNamedFunctions: true, allowUnboundThis: true}]
|
||||||
|
prefer-const: [2, {destructuring: all, ignoreReadBeforeAssign: true}]
|
||||||
|
prefer-destructuring: [0]
|
||||||
|
prefer-exponentiation-operator: [2]
|
||||||
|
prefer-named-capture-group: [0]
|
||||||
|
prefer-numeric-literals: [2]
|
||||||
|
prefer-object-has-own: [0]
|
||||||
|
prefer-object-spread: [2]
|
||||||
|
prefer-promise-reject-errors: [2, {allowEmptyReject: false}]
|
||||||
|
prefer-regex-literals: [2]
|
||||||
|
prefer-rest-params: [2]
|
||||||
|
prefer-spread: [2]
|
||||||
|
prefer-template: [2]
|
||||||
|
quote-props: [0]
|
||||||
|
quotes: [2, single, {avoidEscape: true, allowTemplateLiterals: true}]
|
||||||
|
radix: [2, as-needed]
|
||||||
|
regexp/confusing-quantifier: [2]
|
||||||
|
regexp/control-character-escape: [2]
|
||||||
|
regexp/hexadecimal-escape: [0]
|
||||||
|
regexp/letter-case: [0]
|
||||||
|
regexp/match-any: [2]
|
||||||
|
regexp/negation: [2]
|
||||||
|
regexp/no-contradiction-with-assertion: [0]
|
||||||
|
regexp/no-control-character: [0]
|
||||||
|
regexp/no-dupe-characters-character-class: [2]
|
||||||
|
regexp/no-dupe-disjunctions: [2]
|
||||||
|
regexp/no-empty-alternative: [2]
|
||||||
|
regexp/no-empty-capturing-group: [2]
|
||||||
|
regexp/no-empty-character-class: [0]
|
||||||
|
regexp/no-empty-group: [2]
|
||||||
|
regexp/no-empty-lookarounds-assertion: [2]
|
||||||
|
regexp/no-escape-backspace: [2]
|
||||||
|
regexp/no-extra-lookaround-assertions: [0]
|
||||||
|
regexp/no-invalid-regexp: [2]
|
||||||
|
regexp/no-invisible-character: [2]
|
||||||
|
regexp/no-lazy-ends: [2]
|
||||||
|
regexp/no-legacy-features: [2]
|
||||||
|
regexp/no-misleading-capturing-group: [0]
|
||||||
|
regexp/no-misleading-unicode-character: [0]
|
||||||
|
regexp/no-missing-g-flag: [2]
|
||||||
|
regexp/no-non-standard-flag: [2]
|
||||||
|
regexp/no-obscure-range: [2]
|
||||||
|
regexp/no-octal: [2]
|
||||||
|
regexp/no-optional-assertion: [2]
|
||||||
|
regexp/no-potentially-useless-backreference: [2]
|
||||||
|
regexp/no-standalone-backslash: [2]
|
||||||
|
regexp/no-super-linear-backtracking: [0]
|
||||||
|
regexp/no-super-linear-move: [0]
|
||||||
|
regexp/no-trivially-nested-assertion: [2]
|
||||||
|
regexp/no-trivially-nested-quantifier: [2]
|
||||||
|
regexp/no-unused-capturing-group: [0]
|
||||||
|
regexp/no-useless-assertions: [2]
|
||||||
|
regexp/no-useless-backreference: [2]
|
||||||
|
regexp/no-useless-character-class: [2]
|
||||||
|
regexp/no-useless-dollar-replacements: [2]
|
||||||
|
regexp/no-useless-escape: [2]
|
||||||
|
regexp/no-useless-flag: [2]
|
||||||
|
regexp/no-useless-lazy: [2]
|
||||||
|
regexp/no-useless-non-capturing-group: [2]
|
||||||
|
regexp/no-useless-quantifier: [2]
|
||||||
|
regexp/no-useless-range: [2]
|
||||||
|
regexp/no-useless-two-nums-quantifier: [2]
|
||||||
|
regexp/no-zero-quantifier: [2]
|
||||||
|
regexp/optimal-lookaround-quantifier: [2]
|
||||||
|
regexp/optimal-quantifier-concatenation: [0]
|
||||||
|
regexp/prefer-character-class: [0]
|
||||||
|
regexp/prefer-d: [0]
|
||||||
|
regexp/prefer-escape-replacement-dollar-char: [0]
|
||||||
|
regexp/prefer-lookaround: [0]
|
||||||
|
regexp/prefer-named-backreference: [0]
|
||||||
|
regexp/prefer-named-capture-group: [0]
|
||||||
|
regexp/prefer-named-replacement: [0]
|
||||||
|
regexp/prefer-plus-quantifier: [2]
|
||||||
|
regexp/prefer-predefined-assertion: [2]
|
||||||
|
regexp/prefer-quantifier: [0]
|
||||||
|
regexp/prefer-question-quantifier: [2]
|
||||||
|
regexp/prefer-range: [2]
|
||||||
|
regexp/prefer-regexp-exec: [2]
|
||||||
|
regexp/prefer-regexp-test: [2]
|
||||||
|
regexp/prefer-result-array-groups: [0]
|
||||||
|
regexp/prefer-star-quantifier: [2]
|
||||||
|
regexp/prefer-unicode-codepoint-escapes: [2]
|
||||||
|
regexp/prefer-w: [0]
|
||||||
|
regexp/require-unicode-regexp: [0]
|
||||||
|
regexp/sort-alternatives: [0]
|
||||||
|
regexp/sort-character-class-elements: [0]
|
||||||
|
regexp/sort-flags: [0]
|
||||||
|
regexp/strict: [2]
|
||||||
|
regexp/unicode-escape: [0]
|
||||||
|
regexp/use-ignore-case: [0]
|
||||||
|
require-atomic-updates: [0]
|
||||||
|
require-await: [0]
|
||||||
|
require-unicode-regexp: [0]
|
||||||
|
require-yield: [2]
|
||||||
|
rest-spread-spacing: [2, never]
|
||||||
|
semi-spacing: [2, {before: false, after: true}]
|
||||||
|
semi-style: [2, last]
|
||||||
|
semi: [2, always, {omitLastInOneLineBlock: true}]
|
||||||
|
sonarjs/cognitive-complexity: [0]
|
||||||
|
sonarjs/elseif-without-else: [0]
|
||||||
|
sonarjs/max-switch-cases: [0]
|
||||||
|
sonarjs/no-all-duplicated-branches: [2]
|
||||||
|
sonarjs/no-collapsible-if: [0]
|
||||||
|
sonarjs/no-collection-size-mischeck: [2]
|
||||||
|
sonarjs/no-duplicate-string: [0]
|
||||||
|
sonarjs/no-duplicated-branches: [0]
|
||||||
|
sonarjs/no-element-overwrite: [2]
|
||||||
|
sonarjs/no-empty-collection: [2]
|
||||||
|
sonarjs/no-extra-arguments: [2]
|
||||||
|
sonarjs/no-gratuitous-expressions: [2]
|
||||||
|
sonarjs/no-identical-conditions: [2]
|
||||||
|
sonarjs/no-identical-expressions: [2]
|
||||||
|
sonarjs/no-identical-functions: [2, 5]
|
||||||
|
sonarjs/no-ignored-return: [2]
|
||||||
|
sonarjs/no-inverted-boolean-check: [2]
|
||||||
|
sonarjs/no-nested-switch: [0]
|
||||||
|
sonarjs/no-nested-template-literals: [0]
|
||||||
|
sonarjs/no-one-iteration-loop: [2]
|
||||||
|
sonarjs/no-redundant-boolean: [2]
|
||||||
|
sonarjs/no-redundant-jump: [2]
|
||||||
|
sonarjs/no-same-line-conditional: [2]
|
||||||
|
sonarjs/no-small-switch: [0]
|
||||||
|
sonarjs/no-unused-collection: [2]
|
||||||
|
sonarjs/no-use-of-empty-return-value: [2]
|
||||||
|
sonarjs/no-useless-catch: [2]
|
||||||
|
sonarjs/non-existent-operator: [2]
|
||||||
|
sonarjs/prefer-immediate-return: [0]
|
||||||
|
sonarjs/prefer-object-literal: [0]
|
||||||
|
sonarjs/prefer-single-boolean-return: [0]
|
||||||
|
sonarjs/prefer-while: [2]
|
||||||
|
sort-imports: [0]
|
||||||
|
sort-keys: [0]
|
||||||
|
sort-vars: [0]
|
||||||
|
space-before-blocks: [2, always]
|
||||||
|
space-in-parens: [2, never]
|
||||||
|
space-infix-ops: [2]
|
||||||
|
space-unary-ops: [2]
|
||||||
|
spaced-comment: [2, always]
|
||||||
|
strict: [0]
|
||||||
|
switch-colon-spacing: [2]
|
||||||
|
symbol-description: [2]
|
||||||
|
template-curly-spacing: [2, never]
|
||||||
|
template-tag-spacing: [2, never]
|
||||||
|
unicode-bom: [2, never]
|
||||||
|
unicorn/better-regex: [0]
|
||||||
|
unicorn/catch-error-name: [0]
|
||||||
|
unicorn/consistent-destructuring: [2]
|
||||||
|
unicorn/consistent-function-scoping: [2]
|
||||||
|
unicorn/custom-error-definition: [0]
|
||||||
|
unicorn/empty-brace-spaces: [2]
|
||||||
|
unicorn/error-message: [0]
|
||||||
|
unicorn/escape-case: [0]
|
||||||
|
unicorn/expiring-todo-comments: [0]
|
||||||
|
unicorn/explicit-length-check: [0]
|
||||||
|
unicorn/filename-case: [0]
|
||||||
|
unicorn/import-index: [0]
|
||||||
|
unicorn/import-style: [0]
|
||||||
|
unicorn/new-for-builtins: [2]
|
||||||
|
unicorn/no-abusive-eslint-disable: [0]
|
||||||
|
unicorn/no-array-callback-reference: [0]
|
||||||
|
unicorn/no-array-for-each: [2]
|
||||||
|
unicorn/no-array-method-this-argument: [2]
|
||||||
|
unicorn/no-array-push-push: [2]
|
||||||
|
unicorn/no-array-reduce: [2]
|
||||||
|
unicorn/no-await-expression-member: [0]
|
||||||
|
unicorn/no-console-spaces: [0]
|
||||||
|
unicorn/no-document-cookie: [2]
|
||||||
|
unicorn/no-empty-file: [2]
|
||||||
|
unicorn/no-for-loop: [0]
|
||||||
|
unicorn/no-hex-escape: [0]
|
||||||
|
unicorn/no-instanceof-array: [0]
|
||||||
|
unicorn/no-invalid-remove-event-listener: [2]
|
||||||
|
unicorn/no-keyword-prefix: [0]
|
||||||
|
unicorn/no-lonely-if: [2]
|
||||||
|
unicorn/no-negated-condition: [0]
|
||||||
|
unicorn/no-nested-ternary: [0]
|
||||||
|
unicorn/no-new-array: [0]
|
||||||
|
unicorn/no-new-buffer: [0]
|
||||||
|
unicorn/no-null: [0]
|
||||||
|
unicorn/no-object-as-default-parameter: [0]
|
||||||
|
unicorn/no-process-exit: [0]
|
||||||
|
unicorn/no-static-only-class: [2]
|
||||||
|
unicorn/no-thenable: [2]
|
||||||
|
unicorn/no-this-assignment: [2]
|
||||||
|
unicorn/no-typeof-undefined: [2]
|
||||||
|
unicorn/no-unnecessary-await: [2]
|
||||||
|
unicorn/no-unreadable-array-destructuring: [0]
|
||||||
|
unicorn/no-unreadable-iife: [2]
|
||||||
|
unicorn/no-unsafe-regex: [0]
|
||||||
|
unicorn/no-unused-properties: [2]
|
||||||
|
unicorn/no-useless-fallback-in-spread: [2]
|
||||||
|
unicorn/no-useless-length-check: [2]
|
||||||
|
unicorn/no-useless-promise-resolve-reject: [2]
|
||||||
|
unicorn/no-useless-spread: [2]
|
||||||
|
unicorn/no-useless-switch-case: [2]
|
||||||
|
unicorn/no-useless-undefined: [0]
|
||||||
|
unicorn/no-zero-fractions: [2]
|
||||||
|
unicorn/number-literal-case: [0]
|
||||||
|
unicorn/numeric-separators-style: [0]
|
||||||
|
unicorn/prefer-add-event-listener: [2]
|
||||||
|
unicorn/prefer-array-find: [2]
|
||||||
|
unicorn/prefer-array-flat-map: [2]
|
||||||
|
unicorn/prefer-array-flat: [2]
|
||||||
|
unicorn/prefer-array-index-of: [2]
|
||||||
|
unicorn/prefer-array-some: [2]
|
||||||
|
unicorn/prefer-at: [0]
|
||||||
|
unicorn/prefer-blob-reading-methods: [2]
|
||||||
|
unicorn/prefer-code-point: [0]
|
||||||
|
unicorn/prefer-date-now: [2]
|
||||||
|
unicorn/prefer-default-parameters: [0]
|
||||||
|
unicorn/prefer-dom-node-append: [2]
|
||||||
|
unicorn/prefer-dom-node-dataset: [0]
|
||||||
|
unicorn/prefer-dom-node-remove: [2]
|
||||||
|
unicorn/prefer-dom-node-text-content: [2]
|
||||||
|
unicorn/prefer-event-target: [2]
|
||||||
|
unicorn/prefer-export-from: [2, {ignoreUsedVariables: true}]
|
||||||
|
unicorn/prefer-includes: [2]
|
||||||
|
unicorn/prefer-json-parse-buffer: [0]
|
||||||
|
unicorn/prefer-keyboard-event-key: [2]
|
||||||
|
unicorn/prefer-logical-operator-over-ternary: [2]
|
||||||
|
unicorn/prefer-math-trunc: [2]
|
||||||
|
unicorn/prefer-modern-dom-apis: [0]
|
||||||
|
unicorn/prefer-modern-math-apis: [2]
|
||||||
|
unicorn/prefer-module: [2]
|
||||||
|
unicorn/prefer-native-coercion-functions: [2]
|
||||||
|
unicorn/prefer-negative-index: [2]
|
||||||
|
unicorn/prefer-node-protocol: [2]
|
||||||
|
unicorn/prefer-number-properties: [0]
|
||||||
|
unicorn/prefer-object-from-entries: [2]
|
||||||
|
unicorn/prefer-object-has-own: [0]
|
||||||
|
unicorn/prefer-optional-catch-binding: [2]
|
||||||
|
unicorn/prefer-prototype-methods: [0]
|
||||||
|
unicorn/prefer-query-selector: [0]
|
||||||
|
unicorn/prefer-reflect-apply: [0]
|
||||||
|
unicorn/prefer-regexp-test: [2]
|
||||||
|
unicorn/prefer-set-has: [0]
|
||||||
|
unicorn/prefer-set-size: [2]
|
||||||
|
unicorn/prefer-spread: [0]
|
||||||
|
unicorn/prefer-string-replace-all: [0]
|
||||||
|
unicorn/prefer-string-slice: [0]
|
||||||
|
unicorn/prefer-string-starts-ends-with: [2]
|
||||||
|
unicorn/prefer-string-trim-start-end: [2]
|
||||||
|
unicorn/prefer-switch: [0]
|
||||||
|
unicorn/prefer-ternary: [0]
|
||||||
|
unicorn/prefer-text-content: [2]
|
||||||
|
unicorn/prefer-top-level-await: [0]
|
||||||
|
unicorn/prefer-type-error: [0]
|
||||||
|
unicorn/prevent-abbreviations: [0]
|
||||||
|
unicorn/relative-url-style: [2]
|
||||||
|
unicorn/require-array-join-separator: [2]
|
||||||
|
unicorn/require-number-to-fixed-digits-argument: [2]
|
||||||
|
unicorn/require-post-message-target-origin: [0]
|
||||||
|
unicorn/string-content: [0]
|
||||||
|
unicorn/switch-case-braces: [0]
|
||||||
|
unicorn/template-indent: [2]
|
||||||
|
unicorn/text-encoding-identifier-case: [0]
|
||||||
|
unicorn/throw-new-error: [2]
|
||||||
|
use-isnan: [2]
|
||||||
|
valid-typeof: [2, {requireStringLiterals: true}]
|
||||||
|
vars-on-top: [0]
|
||||||
|
wc/attach-shadow-constructor: [2]
|
||||||
|
wc/guard-super-call: [2]
|
||||||
|
wc/no-closed-shadow-root: [2]
|
||||||
|
wc/no-constructor-attributes: [2]
|
||||||
|
wc/no-constructor-params: [2]
|
||||||
|
wc/no-invalid-element-name: [0] # covered by custom-elements/valid-tag-name
|
||||||
|
wc/no-self-class: [2]
|
||||||
|
wc/no-typos: [2]
|
||||||
|
wc/require-listener-teardown: [2]
|
||||||
|
wrap-iife: [2, inside]
|
||||||
|
wrap-regex: [0]
|
||||||
|
yield-star-spacing: [2, after]
|
||||||
|
yoda: [2, never]
|
||||||
6
.gitattributes
vendored
6
.gitattributes
vendored
@@ -1,10 +1,10 @@
|
|||||||
* text=auto eol=lf
|
* text=auto eol=lf
|
||||||
*.tmpl linguist-language=Handlebars
|
*.tmpl linguist-language=Handlebars
|
||||||
*.pb.go linguist-generated
|
|
||||||
/assets/*.json linguist-generated
|
/assets/*.json linguist-generated
|
||||||
/public/assets/img/svg/*.svg linguist-generated
|
/public/img/svg/*.svg linguist-generated
|
||||||
/templates/swagger/v1_json.tmpl linguist-generated
|
/templates/swagger/v1_json.tmpl linguist-generated
|
||||||
/options/fileicon/** linguist-generated
|
|
||||||
/vendor/** -text -eol linguist-vendored
|
/vendor/** -text -eol linguist-vendored
|
||||||
|
/web_src/fomantic/build/** linguist-generated
|
||||||
|
/web_src/fomantic/_site/globals/site.variables linguist-language=Less
|
||||||
/web_src/js/vendor/** -text -eol linguist-vendored
|
/web_src/js/vendor/** -text -eol linguist-vendored
|
||||||
Dockerfile.* linguist-language=Dockerfile
|
Dockerfile.* linguist-language=Dockerfile
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
<!--
|
<!--
|
||||||
1. Please speak English, this is the language all maintainers can speak and write.
|
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
|
2. Please ask questions or configuration/deploy problems on our Discord
|
||||||
server (https://discord.gg/gitea) or forum (https://forum.gitea.com).
|
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.
|
3. Please take a moment to check that your issue doesn't already exist.
|
||||||
4. Make sure it's not mentioned in the FAQ (https://docs.gitea.com/help/faq)
|
4. Make sure it's not mentioned in the FAQ (https://docs.gitea.io/en-us/faq)
|
||||||
5. Please give all relevant information below for bug reports, because
|
5. Please give all relevant information below for bug reports, because
|
||||||
incomplete details will be handled as an invalid report.
|
incomplete details will be handled as an invalid report.
|
||||||
-->
|
-->
|
||||||
@@ -21,12 +21,12 @@
|
|||||||
- [ ] MySQL
|
- [ ] MySQL
|
||||||
- [ ] MSSQL
|
- [ ] MSSQL
|
||||||
- [ ] SQLite
|
- [ ] SQLite
|
||||||
- Can you reproduce the bug at https://demo.gitea.com:
|
- Can you reproduce the bug at https://try.gitea.io:
|
||||||
- [ ] Yes (provide example URL)
|
- [ ] Yes (provide example URL)
|
||||||
- [ ] No
|
- [ ] No
|
||||||
- Log gist:
|
- Log gist:
|
||||||
<!-- It really is important to provide pertinent logs -->
|
<!-- It really is important to provide pertinent logs -->
|
||||||
<!-- Please read https://docs.gitea.com/administration/logging-config#collecting-logs-for-help -->
|
<!-- Please read https://docs.gitea.io/en-us/logging-configuration/#debugging-problems -->
|
||||||
<!-- In addition, if your problem relates to git commands set `RUN_MODE=dev` at the top of app.ini -->
|
<!-- In addition, if your problem relates to git commands set `RUN_MODE=dev` at the top of app.ini -->
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|||||||
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@@ -1 +1,2 @@
|
|||||||
open_collective: gitea
|
open_collective: gitea
|
||||||
|
custom: https://www.bountysource.com/teams/gitea
|
||||||
|
|||||||
179
.github/ISSUE_TEMPLATE/bug-report.yaml
vendored
179
.github/ISSUE_TEMPLATE/bug-report.yaml
vendored
@@ -1,91 +1,94 @@
|
|||||||
name: Bug Report
|
name: Bug Report
|
||||||
description: Found something you weren't expecting? Report it here!
|
description: Found something you weren't expecting? Report it here!
|
||||||
labels: ["type/bug"]
|
labels: kind/bug
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue.
|
NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue.
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
1. Please speak English, this is the language all maintainers can speak and write.
|
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
|
2. Please ask questions or configuration/deploy problems on our Discord
|
||||||
server (https://discord.gg/gitea) or forum (https://forum.gitea.com).
|
server (https://discord.gg/gitea) or forum (https://discourse.gitea.io).
|
||||||
3. Make sure you are using the latest release and
|
3. Make sure you are using the latest release and
|
||||||
take a moment to check that your issue hasn't been reported before.
|
take a moment to check that your issue hasn't been reported before.
|
||||||
4. Make sure it's not mentioned in the FAQ (https://docs.gitea.com/help/faq)
|
4. Make sure it's not mentioned in the FAQ (https://docs.gitea.io/en-us/faq)
|
||||||
5. It's really important to provide pertinent details and logs (https://docs.gitea.com/help/support),
|
5. Please give all relevant information below for bug reports, because
|
||||||
incomplete details will be handled as an invalid report.
|
incomplete details will be handled as an invalid report.
|
||||||
- type: textarea
|
6. In particular it's really important to provide pertinent logs. You must give us DEBUG level logs.
|
||||||
id: description
|
Please read https://docs.gitea.io/en-us/logging-configuration/#debugging-problems
|
||||||
attributes:
|
In addition, if your problem relates to git commands set `RUN_MODE=dev` at the top of app.ini
|
||||||
label: Description
|
- type: textarea
|
||||||
description: |
|
id: description
|
||||||
Please provide a description of your issue here, with a URL if you were able to reproduce the issue (see below)
|
attributes:
|
||||||
If you are using a proxy or a CDN (e.g. Cloudflare) in front of Gitea, please disable the proxy/CDN fully and access Gitea directly to confirm the issue still persists without those services.
|
label: Description
|
||||||
- type: input
|
description: |
|
||||||
id: gitea-ver
|
Please provide a description of your issue here, with a URL if you were able to reproduce the issue (see below)
|
||||||
attributes:
|
If you are using a proxy or a CDN (e.g. Cloudflare) in front of Gitea, please disable the proxy/CDN fully and access Gitea directly to confirm the issue still persists without those services.
|
||||||
label: Gitea Version
|
- type: input
|
||||||
description: Gitea version (or commit reference) of your instance
|
id: gitea-ver
|
||||||
validations:
|
attributes:
|
||||||
required: true
|
label: Gitea Version
|
||||||
- type: dropdown
|
description: Gitea version (or commit reference) of your instance
|
||||||
id: can-reproduce
|
validations:
|
||||||
attributes:
|
required: true
|
||||||
label: Can you reproduce the bug on the Gitea demo site?
|
- type: dropdown
|
||||||
description: |
|
id: can-reproduce
|
||||||
If so, please provide a URL in the Description field
|
attributes:
|
||||||
URL of Gitea demo: https://demo.gitea.com
|
label: Can you reproduce the bug on the Gitea demo site?
|
||||||
options:
|
description: |
|
||||||
- "Yes"
|
If so, please provide a URL in the Description field
|
||||||
- "No"
|
URL of Gitea demo: https://try.gitea.io
|
||||||
validations:
|
options:
|
||||||
required: true
|
- "Yes"
|
||||||
- type: markdown
|
- "No"
|
||||||
attributes:
|
validations:
|
||||||
value: |
|
required: true
|
||||||
It's really important to provide pertinent logs
|
- type: markdown
|
||||||
Please read https://docs.gitea.com/administration/logging-config#collecting-logs-for-help
|
attributes:
|
||||||
In addition, if your problem relates to git commands set `RUN_MODE=dev` at the top of app.ini
|
value: |
|
||||||
- type: input
|
It's really important to provide pertinent logs
|
||||||
id: logs
|
Please read https://docs.gitea.io/en-us/logging-configuration/#debugging-problems
|
||||||
attributes:
|
In addition, if your problem relates to git commands set `RUN_MODE=dev` at the top of app.ini
|
||||||
label: Log Gist
|
- type: input
|
||||||
description: Please provide a gist URL of your logs, with any sensitive information (e.g. API keys) removed/hidden
|
id: logs
|
||||||
- type: textarea
|
attributes:
|
||||||
id: screenshots
|
label: Log Gist
|
||||||
attributes:
|
description: Please provide a gist URL of your logs, with any sensitive information (e.g. API keys) removed/hidden
|
||||||
label: Screenshots
|
- type: textarea
|
||||||
description: If this issue involves the Web Interface, please provide one or more screenshots
|
id: screenshots
|
||||||
- type: input
|
attributes:
|
||||||
id: git-ver
|
label: Screenshots
|
||||||
attributes:
|
description: If this issue involves the Web Interface, please provide one or more screenshots
|
||||||
label: Git Version
|
- type: input
|
||||||
description: The version of git running on the server
|
id: git-ver
|
||||||
- type: input
|
attributes:
|
||||||
id: os-ver
|
label: Git Version
|
||||||
attributes:
|
description: The version of git running on the server
|
||||||
label: Operating System
|
- type: input
|
||||||
description: The operating system you are using to run Gitea
|
id: os-ver
|
||||||
- type: textarea
|
attributes:
|
||||||
id: run-info
|
label: Operating System
|
||||||
attributes:
|
description: The operating system you are using to run Gitea
|
||||||
label: How are you running Gitea?
|
- type: textarea
|
||||||
description: |
|
id: run-info
|
||||||
Please include information on whether you built Gitea yourself, used one of our downloads, are using https://demo.gitea.com or are using some other package
|
attributes:
|
||||||
Please also tell us how you are running Gitea, e.g. if it is being run from docker, a command-line, systemd etc.
|
label: How are you running Gitea?
|
||||||
If you are using a package or systemd tell us what distribution you are using
|
description: |
|
||||||
validations:
|
Please include information on whether you built Gitea yourself, used one of our downloads, are using https://try.gitea.io or are using some other package
|
||||||
required: true
|
Please also tell us how you are running Gitea, e.g. if it is being run from docker, a command-line, systemd etc.
|
||||||
- type: dropdown
|
If you are using a package or systemd tell us what distribution you are using
|
||||||
id: database
|
validations:
|
||||||
attributes:
|
required: true
|
||||||
label: Database
|
- type: dropdown
|
||||||
description: What database system are you running?
|
id: database
|
||||||
options:
|
attributes:
|
||||||
- PostgreSQL
|
label: Database
|
||||||
- MySQL/MariaDB
|
description: What database system are you running?
|
||||||
- MSSQL
|
options:
|
||||||
- SQLite
|
- PostgreSQL
|
||||||
|
- MySQL
|
||||||
|
- MSSQL
|
||||||
|
- SQLite
|
||||||
|
|||||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
8
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -7,11 +7,11 @@ contact_links:
|
|||||||
url: https://discord.gg/Gitea
|
url: https://discord.gg/Gitea
|
||||||
about: Please ask questions and discuss configuration or deployment problems here.
|
about: Please ask questions and discuss configuration or deployment problems here.
|
||||||
- name: Discourse Forum
|
- name: Discourse Forum
|
||||||
url: https://forum.gitea.com
|
url: https://discourse.gitea.io
|
||||||
about: Questions and configuration or deployment problems can also be discussed on our forum.
|
about: Questions and configuration or deployment problems can also be discussed on our forum.
|
||||||
- name: Frequently Asked Questions
|
- name: Frequently Asked Questions
|
||||||
url: https://docs.gitea.com/help/faq
|
url: https://docs.gitea.io/en-us/faq
|
||||||
about: Please check if your question isn't mentioned here.
|
about: Please check if your question isn't mentioned here.
|
||||||
- name: Crowdin Translations
|
- name: Crowdin Translations
|
||||||
url: https://translate.gitea.com
|
url: https://crowdin.com/project/gitea
|
||||||
about: Translations are managed here.
|
about: Translations are managed here.
|
||||||
|
|||||||
42
.github/ISSUE_TEMPLATE/feature-request.yaml
vendored
42
.github/ISSUE_TEMPLATE/feature-request.yaml
vendored
@@ -1,24 +1,24 @@
|
|||||||
name: Feature Request
|
name: Feature Request
|
||||||
description: Got an idea for a feature that Gitea doesn't have currently? Submit your idea here!
|
description: Got an idea for a feature that Gitea doesn't have currently? Submit your idea here!
|
||||||
labels: ["type/proposal"]
|
labels: ["kind/feature", "kind/proposal"]
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
1. Please speak English, this is the language all maintainers can speak and write.
|
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
|
2. Please ask questions or configuration/deploy problems on our Discord
|
||||||
server (https://discord.gg/gitea) or forum (https://forum.gitea.com).
|
server (https://discord.gg/gitea) or forum (https://discourse.gitea.io).
|
||||||
3. Please take a moment to check that your feature hasn't already been suggested.
|
3. Please take a moment to check that your feature hasn't already been suggested.
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: description
|
id: description
|
||||||
attributes:
|
attributes:
|
||||||
label: Feature Description
|
label: Feature Description
|
||||||
placeholder: |
|
placeholder: |
|
||||||
I think it would be great if Gitea had...
|
I think it would be great if Gitea had...
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: screenshots
|
id: screenshots
|
||||||
attributes:
|
attributes:
|
||||||
label: Screenshots
|
label: Screenshots
|
||||||
description: If you can, provide screenshots of an implementation on another site e.g. GitHub
|
description: If you can, provide screenshots of an implementation on another site e.g. GitHub
|
||||||
|
|||||||
126
.github/ISSUE_TEMPLATE/ui.bug-report.yaml
vendored
126
.github/ISSUE_TEMPLATE/ui.bug-report.yaml
vendored
@@ -1,66 +1,66 @@
|
|||||||
name: Web Interface Bug Report
|
name: Web Interface Bug Report
|
||||||
description: Something doesn't look quite as it should? Report it here!
|
description: Something doesn't look quite as it should? Report it here!
|
||||||
labels: ["type/bug", "topic/ui"]
|
labels: ["kind/bug", "kind/ui"]
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue.
|
NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue.
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
1. Please speak English, this is the language all maintainers can speak and write.
|
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
|
2. Please ask questions or configuration/deploy problems on our Discord
|
||||||
server (https://discord.gg/gitea) or forum (https://forum.gitea.com).
|
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.
|
3. Please take a moment to check that your issue doesn't already exist.
|
||||||
4. Make sure it's not mentioned in the FAQ (https://docs.gitea.com/help/faq)
|
4. Make sure it's not mentioned in the FAQ (https://docs.gitea.io/en-us/faq)
|
||||||
5. Please give all relevant information below for bug reports, because
|
5. Please give all relevant information below for bug reports, because
|
||||||
incomplete details will be handled as an invalid report.
|
incomplete details will be handled as an invalid report.
|
||||||
6. In particular it's really important to provide pertinent logs. If you are certain that this is a javascript
|
6. In particular it's really important to provide pertinent logs. If you are certain that this is a javascript
|
||||||
error, show us the javascript console. If the error appears to relate to Gitea the server you must also give us
|
error, show us the javascript console. If the error appears to relate to Gitea the server you must also give us
|
||||||
DEBUG level logs. (See https://docs.gitea.com/administration/logging-config#collecting-logs-for-help)
|
DEBUG level logs. (See https://docs.gitea.io/en-us/logging-configuration/#debugging-problems)
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: description
|
id: description
|
||||||
attributes:
|
attributes:
|
||||||
label: Description
|
label: Description
|
||||||
description: |
|
description: |
|
||||||
Please provide a description of your issue here, with a URL if you were able to reproduce the issue (see below)
|
Please provide a description of your issue here, with a URL if you were able to reproduce the issue (see below)
|
||||||
If using a proxy or a CDN (e.g. CloudFlare) in front of gitea, please disable the proxy/CDN fully and connect to gitea directly to confirm the issue still persists without those services.
|
If using a proxy or a CDN (e.g. CloudFlare) in front of gitea, please disable the proxy/CDN fully and connect to gitea directly to confirm the issue still persists without those services.
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: screenshots
|
id: screenshots
|
||||||
attributes:
|
attributes:
|
||||||
label: Screenshots
|
label: Screenshots
|
||||||
description: Please provide at least 1 screenshot showing the issue.
|
description: Please provide at least 1 screenshot showing the issue.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
id: gitea-ver
|
id: gitea-ver
|
||||||
attributes:
|
attributes:
|
||||||
label: Gitea Version
|
label: Gitea Version
|
||||||
description: Gitea version (or commit reference) your instance is running
|
description: Gitea version (or commit reference) your instance is running
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
id: can-reproduce
|
id: can-reproduce
|
||||||
attributes:
|
attributes:
|
||||||
label: Can you reproduce the bug on the Gitea demo site?
|
label: Can you reproduce the bug on the Gitea demo site?
|
||||||
description: |
|
description: |
|
||||||
If so, please provide a URL in the Description field
|
If so, please provide a URL in the Description field
|
||||||
URL of Gitea demo: https://demo.gitea.com
|
URL of Gitea demo: https://try.gitea.io
|
||||||
options:
|
options:
|
||||||
- "Yes"
|
- "Yes"
|
||||||
- "No"
|
- "No"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
id: os-ver
|
id: os-ver
|
||||||
attributes:
|
attributes:
|
||||||
label: Operating System
|
label: Operating System
|
||||||
description: The operating system you are using to access Gitea
|
description: The operating system you are using to access Gitea
|
||||||
- type: input
|
- type: input
|
||||||
id: browser-ver
|
id: browser-ver
|
||||||
attributes:
|
attributes:
|
||||||
label: Browser Version
|
label: Browser Version
|
||||||
description: The browser and version that you are using to access Gitea
|
description: The browser and version that you are using to access Gitea
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
7
.github/actionlint.yaml
vendored
7
.github/actionlint.yaml
vendored
@@ -1,7 +0,0 @@
|
|||||||
self-hosted-runner:
|
|
||||||
labels:
|
|
||||||
- actuated-4cpu-8gb
|
|
||||||
- actuated-4cpu-16gb
|
|
||||||
- nscloud
|
|
||||||
- namespace-profile-gitea-release-docker
|
|
||||||
- namespace-profile-gitea-release-binary
|
|
||||||
83
.github/labeler.yml
vendored
83
.github/labeler.yml
vendored
@@ -1,83 +0,0 @@
|
|||||||
modifies/docs:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- "**/*.md"
|
|
||||||
- "docs/**"
|
|
||||||
|
|
||||||
modifies/templates:
|
|
||||||
- changed-files:
|
|
||||||
- all-globs-to-any-file:
|
|
||||||
- "templates/**"
|
|
||||||
- "!templates/swagger/v1_json.tmpl"
|
|
||||||
|
|
||||||
modifies/api:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- "routers/api/**"
|
|
||||||
- "templates/swagger/v1_json.tmpl"
|
|
||||||
|
|
||||||
modifies/cli:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- "cmd/**"
|
|
||||||
|
|
||||||
modifies/translation:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- "options/locale/*.ini"
|
|
||||||
|
|
||||||
modifies/migrations:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- "models/migrations/**"
|
|
||||||
|
|
||||||
modifies/internal:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- ".air.toml"
|
|
||||||
- "Makefile"
|
|
||||||
- "Dockerfile"
|
|
||||||
- "Dockerfile.rootless"
|
|
||||||
- ".dockerignore"
|
|
||||||
- "docker/**"
|
|
||||||
- ".editorconfig"
|
|
||||||
- ".eslintrc.cjs"
|
|
||||||
- ".golangci.yml"
|
|
||||||
- ".gitpod.yml"
|
|
||||||
- ".markdownlint.yaml"
|
|
||||||
- ".spectral.yaml"
|
|
||||||
- "stylelint.config.js"
|
|
||||||
- ".yamllint.yaml"
|
|
||||||
- ".github/**"
|
|
||||||
- ".gitea/**"
|
|
||||||
- ".devcontainer/**"
|
|
||||||
- "build.go"
|
|
||||||
- "build/**"
|
|
||||||
- "contrib/**"
|
|
||||||
|
|
||||||
modifies/dependencies:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- "package.json"
|
|
||||||
- "package-lock.json"
|
|
||||||
- "pyproject.toml"
|
|
||||||
- "poetry.lock"
|
|
||||||
- "go.mod"
|
|
||||||
- "go.sum"
|
|
||||||
|
|
||||||
modifies/go:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- "**/*.go"
|
|
||||||
|
|
||||||
modifies/frontend:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- "*.js"
|
|
||||||
- "*.ts"
|
|
||||||
- "web_src/**"
|
|
||||||
|
|
||||||
docs-update-needed:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file:
|
|
||||||
- "custom/conf/app.example.ini"
|
|
||||||
11
.github/pull_request_template.md
vendored
11
.github/pull_request_template.md
vendored
@@ -1,10 +1,9 @@
|
|||||||
<!-- start tips -->
|
<!-- start tips -->
|
||||||
Please check the following:
|
Please check the following:
|
||||||
1. Make sure you are targeting the `main` branch, pull requests on release branches are only allowed for backports.
|
1. Make sure you are targeting the `main` branch, pull requests on release branches are only allowed for backports.
|
||||||
2. Make sure you have read contributing guidelines: https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md .
|
2. Make sure you have read contributing guidelines: https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md .
|
||||||
3. For documentations contribution, please go to https://gitea.com/gitea/docs
|
3. Describe what your pull request does and which issue you're targeting (if any).
|
||||||
4. Describe what your pull request does and which issue you're targeting (if any).
|
4. It is recommended to enable "Allow edits by maintainers", so maintainers can help more easily.
|
||||||
5. It is recommended to enable "Allow edits by maintainers", so maintainers can help more easily.
|
5. Your input here will be included in the commit message when this PR has been merged. If you don't want some content to be included, please separate them with a line like `---`.
|
||||||
6. Your input here will be included in the commit message when this PR has been merged. If you don't want some content to be included, please separate them with a line like `---`.
|
6. Delete all these tips before posting.
|
||||||
7. Delete all these tips before posting.
|
|
||||||
<!-- end tips -->
|
<!-- end tips -->
|
||||||
|
|||||||
54
.github/stale.yml
vendored
Normal file
54
.github/stale.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# 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.
|
||||||
|
I am here to help clear issues left open even if solved or waiting for more insight.
|
||||||
|
This issue will be closed if no further activity occurs during the next 2 weeks.
|
||||||
|
If the issue is still valid just add a comment to keep it alive.
|
||||||
|
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.
|
||||||
15
.github/workflows/cron-licenses.yml
vendored
15
.github/workflows/cron-licenses.yml
vendored
@@ -1,8 +1,8 @@
|
|||||||
name: cron-licenses
|
name: cron-licenses
|
||||||
|
|
||||||
on:
|
on:
|
||||||
# schedule:
|
schedule:
|
||||||
# - cron: "7 0 * * 1" # every Monday at 00:07 UTC
|
- cron: "7 0 * * 1" # every Monday at 00:07 UTC
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -10,15 +10,14 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'go-gitea/gitea'
|
if: github.repository == 'go-gitea/gitea'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version: ">=1.20.1"
|
||||||
check-latest: true
|
- run: make generate-license generate-gitignore
|
||||||
- run: make generate-gitignore
|
|
||||||
timeout-minutes: 40
|
timeout-minutes: 40
|
||||||
- name: push translations to repo
|
- name: push translations to repo
|
||||||
uses: appleboy/git-push-action@v0.0.3
|
uses: appleboy/git-push-action@v0.0.2
|
||||||
with:
|
with:
|
||||||
author_email: "teabot@gitea.io"
|
author_email: "teabot@gitea.io"
|
||||||
author_name: GiteaBot
|
author_name: GiteaBot
|
||||||
|
|||||||
22
.github/workflows/cron-lock.yml
vendored
Normal file
22
.github/workflows/cron-lock.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
name: cron-lock
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0 * * *" # every day at 00:00 UTC
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: lock
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
action:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository == 'go-gitea/gitea'
|
||||||
|
steps:
|
||||||
|
- uses: dessant/lock-threads@v4
|
||||||
|
with:
|
||||||
|
issue-inactive-days: 45
|
||||||
37
.github/workflows/cron-translations.yml
vendored
37
.github/workflows/cron-translations.yml
vendored
@@ -10,24 +10,19 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'go-gitea/gitea'
|
if: github.repository == 'go-gitea/gitea'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: crowdin/github-action@v1
|
- name: download from crowdin
|
||||||
with:
|
uses: docker://jonasfranz/crowdin
|
||||||
upload_sources: true
|
|
||||||
upload_translations: false
|
|
||||||
download_sources: false
|
|
||||||
download_translations: true
|
|
||||||
push_translations: false
|
|
||||||
push_sources: false
|
|
||||||
create_pull_request: false
|
|
||||||
config: crowdin.yml
|
|
||||||
env:
|
env:
|
||||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
|
||||||
CROWDIN_KEY: ${{ secrets.CROWDIN_KEY }}
|
CROWDIN_KEY: ${{ secrets.CROWDIN_KEY }}
|
||||||
|
PLUGIN_DOWNLOAD: true
|
||||||
|
PLUGIN_EXPORT_DIR: options/locale/
|
||||||
|
PLUGIN_IGNORE_BRANCH: true
|
||||||
|
PLUGIN_PROJECT_IDENTIFIER: gitea
|
||||||
- name: update locales
|
- name: update locales
|
||||||
run: ./build/update-locales.sh
|
run: ./build/update-locales.sh
|
||||||
- name: push translations to repo
|
- name: push translations to repo
|
||||||
uses: appleboy/git-push-action@v0.0.3
|
uses: appleboy/git-push-action@v0.0.2
|
||||||
with:
|
with:
|
||||||
author_email: "teabot@gitea.io"
|
author_email: "teabot@gitea.io"
|
||||||
author_name: GiteaBot
|
author_name: GiteaBot
|
||||||
@@ -36,3 +31,19 @@ jobs:
|
|||||||
commit_message: "[skip ci] Updated translations via Crowdin"
|
commit_message: "[skip ci] Updated translations via Crowdin"
|
||||||
remote: "git@github.com:go-gitea/gitea.git"
|
remote: "git@github.com:go-gitea/gitea.git"
|
||||||
ssh_key: ${{ secrets.DEPLOY_KEY }}
|
ssh_key: ${{ secrets.DEPLOY_KEY }}
|
||||||
|
crowdin-push:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository == 'go-gitea/gitea'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: push translations to crowdin
|
||||||
|
uses: docker://jonasfranz/crowdin
|
||||||
|
env:
|
||||||
|
CROWDIN_KEY: ${{ secrets.CROWDIN_KEY }}
|
||||||
|
PLUGIN_UPLOAD: true
|
||||||
|
PLUGIN_EXPORT_DIR: options/locale/
|
||||||
|
PLUGIN_IGNORE_BRANCH: true
|
||||||
|
PLUGIN_PROJECT_IDENTIFIER: gitea
|
||||||
|
PLUGIN_FILES: |
|
||||||
|
locale_en-US.ini: options/locale/locale_en-US.ini
|
||||||
|
PLUGIN_BRANCH: main
|
||||||
|
|||||||
70
.github/workflows/files-changed.yml
vendored
70
.github/workflows/files-changed.yml
vendored
@@ -4,98 +4,50 @@ on:
|
|||||||
workflow_call:
|
workflow_call:
|
||||||
outputs:
|
outputs:
|
||||||
backend:
|
backend:
|
||||||
|
description: "whether backend files changed"
|
||||||
value: ${{ jobs.detect.outputs.backend }}
|
value: ${{ jobs.detect.outputs.backend }}
|
||||||
frontend:
|
frontend:
|
||||||
|
description: "whether frontend files changed"
|
||||||
value: ${{ jobs.detect.outputs.frontend }}
|
value: ${{ jobs.detect.outputs.frontend }}
|
||||||
docs:
|
docs:
|
||||||
|
description: "whether docs files changed"
|
||||||
value: ${{ jobs.detect.outputs.docs }}
|
value: ${{ jobs.detect.outputs.docs }}
|
||||||
actions:
|
actions:
|
||||||
|
description: "whether actions files changed"
|
||||||
value: ${{ jobs.detect.outputs.actions }}
|
value: ${{ jobs.detect.outputs.actions }}
|
||||||
templates:
|
|
||||||
value: ${{ jobs.detect.outputs.templates }}
|
|
||||||
docker:
|
|
||||||
value: ${{ jobs.detect.outputs.docker }}
|
|
||||||
swagger:
|
|
||||||
value: ${{ jobs.detect.outputs.swagger }}
|
|
||||||
yaml:
|
|
||||||
value: ${{ jobs.detect.outputs.yaml }}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
detect:
|
detect:
|
||||||
|
name: detect which files changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 3
|
timeout-minutes: 3
|
||||||
|
# Map a step output to a job output
|
||||||
outputs:
|
outputs:
|
||||||
backend: ${{ steps.changes.outputs.backend }}
|
backend: ${{ steps.changes.outputs.backend }}
|
||||||
frontend: ${{ steps.changes.outputs.frontend }}
|
frontend: ${{ steps.changes.outputs.frontend }}
|
||||||
docs: ${{ steps.changes.outputs.docs }}
|
docs: ${{ steps.changes.outputs.docs }}
|
||||||
actions: ${{ steps.changes.outputs.actions }}
|
actions: ${{ steps.changes.outputs.actions }}
|
||||||
templates: ${{ steps.changes.outputs.templates }}
|
|
||||||
docker: ${{ steps.changes.outputs.docker }}
|
|
||||||
swagger: ${{ steps.changes.outputs.swagger }}
|
|
||||||
yaml: ${{ steps.changes.outputs.yaml }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: dorny/paths-filter@v3
|
- uses: dorny/paths-filter@v2
|
||||||
id: changes
|
id: changes
|
||||||
with:
|
with:
|
||||||
filters: |
|
filters: |
|
||||||
backend:
|
backend:
|
||||||
- "**/*.go"
|
- "**/*.go"
|
||||||
- "templates/**/*.tmpl"
|
- "**/*.tmpl"
|
||||||
- "assets/emoji.json"
|
|
||||||
- "go.mod"
|
- "go.mod"
|
||||||
- "go.sum"
|
- "go.sum"
|
||||||
- "Makefile"
|
|
||||||
- ".golangci.yml"
|
|
||||||
- ".editorconfig"
|
|
||||||
- "options/locale/locale_en-US.ini"
|
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
- "*.js"
|
- "**/*.js"
|
||||||
- "*.ts"
|
|
||||||
- "web_src/**"
|
- "web_src/**"
|
||||||
- "tools/*.js"
|
|
||||||
- "tools/*.ts"
|
|
||||||
- "assets/emoji.json"
|
|
||||||
- "package.json"
|
- "package.json"
|
||||||
- "package-lock.json"
|
- "package-lock.json"
|
||||||
- "Makefile"
|
|
||||||
- ".eslintrc.cjs"
|
|
||||||
- ".npmrc"
|
|
||||||
|
|
||||||
docs:
|
docs:
|
||||||
- "**/*.md"
|
- "**/*.md"
|
||||||
- ".markdownlint.yaml"
|
- "docs/**"
|
||||||
- "package.json"
|
|
||||||
- "package-lock.json"
|
|
||||||
|
|
||||||
actions:
|
actions:
|
||||||
- ".github/workflows/*"
|
- ".github/workflows/*"
|
||||||
- "Makefile"
|
|
||||||
|
|
||||||
templates:
|
|
||||||
- "tools/lint-templates-*.js"
|
|
||||||
- "templates/**/*.tmpl"
|
|
||||||
- "pyproject.toml"
|
|
||||||
- "poetry.lock"
|
|
||||||
|
|
||||||
docker:
|
|
||||||
- "Dockerfile"
|
|
||||||
- "Dockerfile.rootless"
|
|
||||||
- "docker/**"
|
|
||||||
- "Makefile"
|
|
||||||
|
|
||||||
swagger:
|
|
||||||
- "templates/swagger/v1_json.tmpl"
|
|
||||||
- "templates/swagger/v1_input.json"
|
|
||||||
- "Makefile"
|
|
||||||
- "package.json"
|
|
||||||
- "package-lock.json"
|
|
||||||
- ".spectral.yaml"
|
|
||||||
|
|
||||||
yaml:
|
|
||||||
- "**/*.yml"
|
|
||||||
- "**/*.yaml"
|
|
||||||
- ".yamllint.yaml"
|
|
||||||
- "pyproject.toml"
|
|
||||||
- "poetry.lock"
|
|
||||||
|
|||||||
117
.github/workflows/pull-compliance.yml
vendored
117
.github/workflows/pull-compliance.yml
vendored
@@ -16,86 +16,28 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version: ">=1.20"
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- run: make deps-backend deps-tools
|
- run: make deps-backend deps-tools
|
||||||
- run: make lint-backend
|
- run: make lint-backend
|
||||||
env:
|
env:
|
||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
TAGS: bindata sqlite sqlite_unlock_notify
|
||||||
|
|
||||||
lint-templates:
|
|
||||||
if: needs.files-changed.outputs.templates == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: "3.12"
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 22
|
|
||||||
cache: npm
|
|
||||||
cache-dependency-path: package-lock.json
|
|
||||||
- run: pip install poetry
|
|
||||||
- run: make deps-py
|
|
||||||
- run: make deps-frontend
|
|
||||||
- run: make lint-templates
|
|
||||||
|
|
||||||
lint-yaml:
|
|
||||||
if: needs.files-changed.outputs.yaml == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: "3.12"
|
|
||||||
- run: pip install poetry
|
|
||||||
- run: make deps-py
|
|
||||||
- run: make lint-yaml
|
|
||||||
|
|
||||||
lint-swagger:
|
|
||||||
if: needs.files-changed.outputs.swagger == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 22
|
|
||||||
cache: npm
|
|
||||||
cache-dependency-path: package-lock.json
|
|
||||||
- run: make deps-frontend
|
|
||||||
- run: make lint-swagger
|
|
||||||
|
|
||||||
lint-spell:
|
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true' || needs.files-changed.outputs.docs == 'true' || needs.files-changed.outputs.templates == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- run: make lint-spell
|
|
||||||
|
|
||||||
lint-go-windows:
|
lint-go-windows:
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version: ">=1.20"
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- run: make deps-backend deps-tools
|
- run: make deps-backend deps-tools
|
||||||
- run: make lint-go-windows lint-go-gitea-vet
|
- run: make lint-go-windows lint-go-vet
|
||||||
env:
|
env:
|
||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
TAGS: bindata sqlite sqlite_unlock_notify
|
||||||
GOOS: windows
|
GOOS: windows
|
||||||
@@ -106,10 +48,10 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version: ">=1.20"
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- run: make deps-backend deps-tools
|
- run: make deps-backend deps-tools
|
||||||
- run: make lint-go
|
- run: make lint-go
|
||||||
@@ -121,10 +63,10 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version: ">=1.20"
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- run: make deps-backend deps-tools
|
- run: make deps-backend deps-tools
|
||||||
- run: make --always-make checks-backend # ensure the "go-licenses" make target runs
|
- run: make --always-make checks-backend # ensure the "go-licenses" make target runs
|
||||||
@@ -134,16 +76,13 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: 20
|
||||||
cache: npm
|
|
||||||
cache-dependency-path: package-lock.json
|
|
||||||
- run: make deps-frontend
|
- run: make deps-frontend
|
||||||
- run: make lint-frontend
|
- run: make lint-frontend
|
||||||
- run: make checks-frontend
|
- run: make checks-frontend
|
||||||
- run: make test-frontend
|
|
||||||
- run: make frontend
|
- run: make frontend
|
||||||
|
|
||||||
backend:
|
backend:
|
||||||
@@ -151,14 +90,14 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version: ">=1.20"
|
||||||
check-latest: true
|
check-latest: true
|
||||||
# no frontend build here as backend should be able to build
|
# no frontend build here as backend should be able to build
|
||||||
# even without any frontend files
|
# even without any frontend files
|
||||||
- run: make deps-backend
|
- run: make deps-backend deps-tools
|
||||||
- run: go build -o gitea_no_gcc # test if build succeeds without the sqlite tag
|
- run: go build -o gitea_no_gcc # test if build succeeds without the sqlite tag
|
||||||
- name: build-backend-arm64
|
- name: build-backend-arm64
|
||||||
run: make backend # test cross compile
|
run: make backend # test cross compile
|
||||||
@@ -183,23 +122,19 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: 20
|
||||||
cache: npm
|
|
||||||
cache-dependency-path: package-lock.json
|
|
||||||
- run: make deps-frontend
|
- run: make deps-frontend
|
||||||
- run: make lint-md
|
- run: make lint-md
|
||||||
|
- run: make docs # test if build could succeed
|
||||||
|
|
||||||
actions:
|
actions:
|
||||||
if: needs.files-changed.outputs.actions == 'true' || needs.files-changed.outputs.actions == 'true'
|
if: needs.files-changed.outputs.actions == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v4
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- run: make lint-actions
|
- run: make lint-actions
|
||||||
|
|||||||
134
.github/workflows/pull-db-tests.yml
vendored
134
.github/workflows/pull-db-tests.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
pgsql:
|
pgsql:
|
||||||
image: postgres:12
|
image: postgres:15
|
||||||
env:
|
env:
|
||||||
POSTGRES_DB: test
|
POSTGRES_DB: test
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
@@ -31,28 +31,24 @@ jobs:
|
|||||||
minio:
|
minio:
|
||||||
# as github actions doesn't support "entrypoint", we need to use a non-official image
|
# as github actions doesn't support "entrypoint", we need to use a non-official image
|
||||||
# that has a custom entrypoint set to "minio server /data"
|
# that has a custom entrypoint set to "minio server /data"
|
||||||
image: bitnami/minio:2023.8.31
|
image: bitnami/minio:2021.3.17
|
||||||
env:
|
env:
|
||||||
MINIO_ROOT_USER: 123456
|
MINIO_ACCESS_KEY: 123456
|
||||||
MINIO_ROOT_PASSWORD: 12345678
|
MINIO_SECRET_KEY: 12345678
|
||||||
ports:
|
ports:
|
||||||
- "9000:9000"
|
- "9000:9000"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version: ">=1.20.0"
|
||||||
check-latest: true
|
|
||||||
- name: Add hosts to /etc/hosts
|
- name: Add hosts to /etc/hosts
|
||||||
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 pgsql ldap minio" | sudo tee -a /etc/hosts'
|
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 pgsql ldap minio" | sudo tee -a /etc/hosts'
|
||||||
- run: make deps-backend
|
- run: make deps-backend
|
||||||
- run: make backend
|
- run: make backend
|
||||||
env:
|
env:
|
||||||
TAGS: bindata
|
TAGS: bindata
|
||||||
- name: run migration tests
|
- run: make test-pgsql-migration test-pgsql
|
||||||
run: make test-pgsql-migration
|
|
||||||
- name: run tests
|
|
||||||
run: make test-pgsql
|
|
||||||
timeout-minutes: 50
|
timeout-minutes: 50
|
||||||
env:
|
env:
|
||||||
TAGS: bindata gogit
|
TAGS: bindata gogit
|
||||||
@@ -66,19 +62,15 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version: ">=1.20.0"
|
||||||
check-latest: true
|
|
||||||
- run: make deps-backend
|
- run: make deps-backend
|
||||||
- run: make backend
|
- run: make backend
|
||||||
env:
|
env:
|
||||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||||
- name: run migration tests
|
- run: make test-sqlite-migration test-sqlite
|
||||||
run: make test-sqlite-migration
|
|
||||||
- name: run tests
|
|
||||||
run: make test-sqlite
|
|
||||||
timeout-minutes: 50
|
timeout-minutes: 50
|
||||||
env:
|
env:
|
||||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||||
@@ -91,18 +83,26 @@ jobs:
|
|||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
|
mysql:
|
||||||
|
image: mysql:5.7
|
||||||
|
env:
|
||||||
|
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||||
|
MYSQL_DATABASE: test
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
elasticsearch:
|
elasticsearch:
|
||||||
image: elasticsearch:7.5.0
|
image: elasticsearch:7.5.0
|
||||||
env:
|
env:
|
||||||
discovery.type: single-node
|
discovery.type: single-node
|
||||||
ports:
|
ports:
|
||||||
- "9200:9200"
|
- "9200:9200"
|
||||||
meilisearch:
|
smtpimap:
|
||||||
image: getmeili/meilisearch:v1
|
image: tabascoterrier/docker-imap-devel:latest
|
||||||
env:
|
|
||||||
MEILI_ENV: development # disable auth
|
|
||||||
ports:
|
ports:
|
||||||
- "7700:7700"
|
- "25:25"
|
||||||
|
- "143:143"
|
||||||
|
- "587:587"
|
||||||
|
- "993:993"
|
||||||
redis:
|
redis:
|
||||||
image: redis
|
image: redis
|
||||||
options: >- # wait until redis has started
|
options: >- # wait until redis has started
|
||||||
@@ -119,18 +119,13 @@ jobs:
|
|||||||
MINIO_SECRET_KEY: 12345678
|
MINIO_SECRET_KEY: 12345678
|
||||||
ports:
|
ports:
|
||||||
- "9000:9000"
|
- "9000:9000"
|
||||||
devstoreaccount1.azurite.local: # https://github.com/Azure/Azurite/issues/1583
|
|
||||||
image: mcr.microsoft.com/azure-storage/azurite:latest
|
|
||||||
ports:
|
|
||||||
- 10000:10000
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version: ">=1.20.0"
|
||||||
check-latest: true
|
|
||||||
- name: Add hosts to /etc/hosts
|
- name: Add hosts to /etc/hosts
|
||||||
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 minio devstoreaccount1.azurite.local mysql elasticsearch meilisearch smtpimap" | sudo tee -a /etc/hosts'
|
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mysql elasticsearch smtpimap" | sudo tee -a /etc/hosts'
|
||||||
- run: make deps-backend
|
- run: make deps-backend
|
||||||
- run: make backend
|
- run: make backend
|
||||||
env:
|
env:
|
||||||
@@ -148,21 +143,18 @@ jobs:
|
|||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
|
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
|
||||||
|
|
||||||
test-mysql:
|
test-mysql5:
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
mysql:
|
mysql:
|
||||||
# the bitnami mysql image has more options than the official one, it's easier to customize
|
image: mysql:5.7
|
||||||
image: bitnami/mysql:8.0
|
|
||||||
env:
|
env:
|
||||||
ALLOW_EMPTY_PASSWORD: true
|
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||||
MYSQL_DATABASE: testgitea
|
MYSQL_DATABASE: test
|
||||||
ports:
|
ports:
|
||||||
- "3306:3306"
|
- "3306:3306"
|
||||||
options: >-
|
|
||||||
--mount type=tmpfs,destination=/bitnami/mysql/data
|
|
||||||
elasticsearch:
|
elasticsearch:
|
||||||
image: elasticsearch:7.5.0
|
image: elasticsearch:7.5.0
|
||||||
env:
|
env:
|
||||||
@@ -177,60 +169,78 @@ jobs:
|
|||||||
- "587:587"
|
- "587:587"
|
||||||
- "993:993"
|
- "993:993"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version: ">=1.20.0"
|
||||||
check-latest: true
|
|
||||||
- name: Add hosts to /etc/hosts
|
- name: Add hosts to /etc/hosts
|
||||||
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mysql elasticsearch smtpimap" | sudo tee -a /etc/hosts'
|
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mysql elasticsearch smtpimap" | sudo tee -a /etc/hosts'
|
||||||
- run: make deps-backend
|
- run: make deps-backend
|
||||||
- run: make backend
|
- run: make backend
|
||||||
env:
|
env:
|
||||||
TAGS: bindata
|
TAGS: bindata
|
||||||
- name: run migration tests
|
|
||||||
run: make test-mysql-migration
|
|
||||||
- name: run tests
|
- name: run tests
|
||||||
# run: make integration-test-coverage (at the moment, no coverage is really handled)
|
run: make test-mysql-migration integration-test-coverage
|
||||||
run: make test-mysql
|
|
||||||
env:
|
env:
|
||||||
TAGS: bindata
|
TAGS: bindata
|
||||||
RACE_ENABLED: true
|
RACE_ENABLED: true
|
||||||
USE_REPO_TEST_DIR: 1
|
USE_REPO_TEST_DIR: 1
|
||||||
TEST_INDEXER_CODE_ES_URL: "http://elastic:changeme@elasticsearch:9200"
|
TEST_INDEXER_CODE_ES_URL: "http://elastic:changeme@elasticsearch:9200"
|
||||||
|
|
||||||
|
test-mysql8:
|
||||||
|
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||||
|
needs: files-changed
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
services:
|
||||||
|
mysql8:
|
||||||
|
image: mysql:8
|
||||||
|
env:
|
||||||
|
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||||
|
MYSQL_DATABASE: testgitea
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: ">=1.20.0"
|
||||||
|
- name: Add hosts to /etc/hosts
|
||||||
|
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mysql8" | sudo tee -a /etc/hosts'
|
||||||
|
- run: make deps-backend
|
||||||
|
- run: make backend
|
||||||
|
env:
|
||||||
|
TAGS: bindata
|
||||||
|
- run: make test-mysql8-migration test-mysql8
|
||||||
|
timeout-minutes: 50
|
||||||
|
env:
|
||||||
|
TAGS: bindata
|
||||||
|
USE_REPO_TEST_DIR: 1
|
||||||
|
|
||||||
test-mssql:
|
test-mssql:
|
||||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
services:
|
services:
|
||||||
mssql:
|
mssql:
|
||||||
image: mcr.microsoft.com/mssql/server:2019-latest
|
image: mcr.microsoft.com/mssql/server:latest
|
||||||
env:
|
env:
|
||||||
ACCEPT_EULA: Y
|
ACCEPT_EULA: Y
|
||||||
MSSQL_PID: Standard
|
MSSQL_PID: Standard
|
||||||
SA_PASSWORD: MwantsaSecurePassword1
|
SA_PASSWORD: MwantsaSecurePassword1
|
||||||
ports:
|
ports:
|
||||||
- "1433:1433"
|
- "1433:1433"
|
||||||
devstoreaccount1.azurite.local: # https://github.com/Azure/Azurite/issues/1583
|
|
||||||
image: mcr.microsoft.com/azure-storage/azurite:latest
|
|
||||||
ports:
|
|
||||||
- 10000:10000
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version: ">=1.20.0"
|
||||||
check-latest: true
|
|
||||||
- name: Add hosts to /etc/hosts
|
- name: Add hosts to /etc/hosts
|
||||||
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mssql devstoreaccount1.azurite.local" | sudo tee -a /etc/hosts'
|
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mssql" | sudo tee -a /etc/hosts'
|
||||||
- run: make deps-backend
|
- run: make deps-backend
|
||||||
- run: make backend
|
- run: make backend
|
||||||
env:
|
env:
|
||||||
TAGS: bindata
|
TAGS: bindata
|
||||||
- run: make test-mssql-migration
|
- run: make test-mssql-migration test-mssql
|
||||||
- name: run tests
|
|
||||||
run: make test-mssql
|
|
||||||
timeout-minutes: 50
|
timeout-minutes: 50
|
||||||
env:
|
env:
|
||||||
TAGS: bindata
|
TAGS: bindata
|
||||||
|
|||||||
20
.github/workflows/pull-docker-dryrun.yml
vendored
20
.github/workflows/pull-docker-dryrun.yml
vendored
@@ -11,25 +11,13 @@ jobs:
|
|||||||
files-changed:
|
files-changed:
|
||||||
uses: ./.github/workflows/files-changed.yml
|
uses: ./.github/workflows/files-changed.yml
|
||||||
|
|
||||||
regular:
|
docker-dryrun:
|
||||||
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.actions == 'true'
|
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: docker/setup-buildx-action@v3
|
- uses: docker/setup-buildx-action@v2
|
||||||
- uses: docker/build-push-action@v5
|
- uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
push: false
|
push: false
|
||||||
tags: gitea/gitea:linux-amd64
|
tags: gitea/gitea:linux-amd64
|
||||||
|
|
||||||
rootless:
|
|
||||||
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
needs: files-changed
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: docker/setup-buildx-action@v3
|
|
||||||
- uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
push: false
|
|
||||||
file: Dockerfile.rootless
|
|
||||||
tags: gitea/gitea:linux-amd64
|
|
||||||
|
|||||||
16
.github/workflows/pull-e2e-tests.yml
vendored
16
.github/workflows/pull-e2e-tests.yml
vendored
@@ -12,22 +12,18 @@ jobs:
|
|||||||
uses: ./.github/workflows/files-changed.yml
|
uses: ./.github/workflows/files-changed.yml
|
||||||
|
|
||||||
test-e2e:
|
test-e2e:
|
||||||
# the "test-e2e" won't pass, and it seems that there is no useful test, so skip
|
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||||
# if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true'
|
|
||||||
if: false
|
|
||||||
needs: files-changed
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version: ">=1.20"
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: 20
|
||||||
cache: npm
|
|
||||||
cache-dependency-path: package-lock.json
|
|
||||||
- run: make deps-frontend frontend deps-backend
|
- run: make deps-frontend frontend deps-backend
|
||||||
- run: npx playwright install --with-deps
|
- run: npx playwright install --with-deps
|
||||||
- run: make test-e2e-sqlite
|
- run: make test-e2e-sqlite
|
||||||
|
|||||||
20
.github/workflows/pull-labeler.yml
vendored
20
.github/workflows/pull-labeler.yml
vendored
@@ -1,20 +0,0 @@
|
|||||||
name: labeler
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
labeler:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
steps:
|
|
||||||
- uses: actions/labeler@v5
|
|
||||||
with:
|
|
||||||
sync-labels: true
|
|
||||||
120
.github/workflows/release-nightly.yml
vendored
120
.github/workflows/release-nightly.yml
vendored
@@ -1,30 +1,24 @@
|
|||||||
name: release-nightly
|
name: release-nightly-assets
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main, release/v*]
|
branches: [ main, release/v* ]
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
nightly-binary:
|
nightly-binary:
|
||||||
runs-on: namespace-profile-gitea-release-binary
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||||
- run: git fetch --unshallow --quiet --tags --force
|
- run: git fetch --unshallow --quiet --tags --force
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version: ">=1.20"
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 22
|
node-version: 20
|
||||||
cache: npm
|
|
||||||
cache-dependency-path: package-lock.json
|
|
||||||
- run: make deps-frontend deps-backend
|
- run: make deps-frontend deps-backend
|
||||||
# xgo build
|
# xgo build
|
||||||
- run: make release
|
- run: make release
|
||||||
@@ -32,7 +26,7 @@ jobs:
|
|||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
TAGS: bindata sqlite sqlite_unlock_notify
|
||||||
- name: import gpg key
|
- name: import gpg key
|
||||||
id: import_gpg
|
id: import_gpg
|
||||||
uses: crazy-max/ghaction-import-gpg@v6
|
uses: crazy-max/ghaction-import-gpg@v5
|
||||||
with:
|
with:
|
||||||
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
|
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
|
||||||
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
||||||
@@ -47,31 +41,25 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
||||||
echo "Cleaned name is ${REF_NAME}"
|
echo "Cleaned name is ${REF_NAME}"
|
||||||
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
|
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||||
- name: configure aws
|
|
||||||
uses: aws-actions/configure-aws-credentials@v4
|
|
||||||
with:
|
|
||||||
aws-region: ${{ secrets.AWS_REGION }}
|
|
||||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
||||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
||||||
- name: upload binaries to s3
|
- name: upload binaries to s3
|
||||||
run: |
|
uses: jakejarvis/s3-sync-action@master
|
||||||
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
|
env:
|
||||||
nightly-docker-rootful:
|
AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
|
||||||
runs-on: namespace-profile-gitea-release-docker
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
permissions:
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
packages: write # to publish to ghcr.io
|
AWS_REGION: ${{ secrets.AWS_REGION }}
|
||||||
|
SOURCE_DIR: dist/release
|
||||||
|
DEST_DIR: gitea/${{ steps.clean_name.outputs.branch }}
|
||||||
|
nightly-docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
||||||
- run: git fetch --unshallow --quiet --tags --force
|
- run: git fetch --unshallow --quiet --tags --force
|
||||||
- uses: actions/setup-go@v5
|
- uses: docker/setup-qemu-action@v2
|
||||||
with:
|
- uses: docker/setup-buildx-action@v2
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- uses: docker/setup-qemu-action@v3
|
|
||||||
- uses: docker/setup-buildx-action@v3
|
|
||||||
- name: Get cleaned branch name
|
- name: Get cleaned branch name
|
||||||
id: clean_name
|
id: clean_name
|
||||||
run: |
|
run: |
|
||||||
@@ -83,72 +71,22 @@ jobs:
|
|||||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
||||||
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
|
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
- name: Login to GHCR using PAT
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: fetch go modules
|
|
||||||
run: make vendor
|
|
||||||
- name: build rootful docker image
|
- name: build rootful docker image
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: |-
|
tags: gitea/gitea:${{ steps.clean_name.outputs.branch }}
|
||||||
gitea/gitea:${{ steps.clean_name.outputs.branch }}
|
|
||||||
ghcr.io/go-gitea/gitea:${{ steps.clean_name.outputs.branch }}
|
|
||||||
nightly-docker-rootless:
|
|
||||||
runs-on: namespace-profile-gitea-release-docker
|
|
||||||
permissions:
|
|
||||||
packages: write # to publish to ghcr.io
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
|
||||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
|
||||||
- run: git fetch --unshallow --quiet --tags --force
|
|
||||||
- uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- uses: docker/setup-qemu-action@v3
|
|
||||||
- uses: docker/setup-buildx-action@v3
|
|
||||||
- name: Get cleaned branch name
|
|
||||||
id: clean_name
|
|
||||||
run: |
|
|
||||||
# if main then say nightly otherwise cleanup name
|
|
||||||
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
|
|
||||||
echo "branch=nightly" >> "$GITHUB_OUTPUT"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
|
||||||
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
- name: Login to GHCR using PAT
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: fetch go modules
|
|
||||||
run: make vendor
|
|
||||||
- name: build rootless docker image
|
- name: build rootless docker image
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v4
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
file: Dockerfile.rootless
|
file: Dockerfile.rootless
|
||||||
tags: |-
|
tags: gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
|
||||||
gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
|
|
||||||
ghcr.io/go-gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
|
|
||||||
|
|||||||
154
.github/workflows/release-tag-rc.yml
vendored
154
.github/workflows/release-tag-rc.yml
vendored
@@ -1,154 +0,0 @@
|
|||||||
name: release-tag-rc
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v1*-rc*"
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
binary:
|
|
||||||
runs-on: namespace-profile-gitea-release-binary
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
|
||||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
|
||||||
- run: git fetch --unshallow --quiet --tags --force
|
|
||||||
- uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 22
|
|
||||||
cache: npm
|
|
||||||
cache-dependency-path: package-lock.json
|
|
||||||
- run: make deps-frontend deps-backend
|
|
||||||
# xgo build
|
|
||||||
- run: make release
|
|
||||||
env:
|
|
||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
|
||||||
- name: import gpg key
|
|
||||||
id: import_gpg
|
|
||||||
uses: crazy-max/ghaction-import-gpg@v6
|
|
||||||
with:
|
|
||||||
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
|
|
||||||
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
|
||||||
- name: sign binaries
|
|
||||||
run: |
|
|
||||||
for f in dist/release/*; do
|
|
||||||
echo '${{ secrets.GPGSIGN_PASSPHRASE }}' | gpg --pinentry-mode loopback --passphrase-fd 0 --batch --yes --detach-sign -u ${{ steps.import_gpg.outputs.fingerprint }} --output "$f.asc" "$f"
|
|
||||||
done
|
|
||||||
# clean branch name to get the folder name in S3
|
|
||||||
- name: Get cleaned branch name
|
|
||||||
id: clean_name
|
|
||||||
run: |
|
|
||||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\/v//' -e 's/release\/v//')
|
|
||||||
echo "Cleaned name is ${REF_NAME}"
|
|
||||||
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
|
||||||
- name: configure aws
|
|
||||||
uses: aws-actions/configure-aws-credentials@v4
|
|
||||||
with:
|
|
||||||
aws-region: ${{ secrets.AWS_REGION }}
|
|
||||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
||||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
||||||
- name: upload binaries to s3
|
|
||||||
run: |
|
|
||||||
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
|
|
||||||
- name: Install GH CLI
|
|
||||||
uses: dev-hanz-ops/install-gh-cli-action@v0.1.0
|
|
||||||
with:
|
|
||||||
gh-cli-version: 2.39.1
|
|
||||||
- name: create github release
|
|
||||||
run: |
|
|
||||||
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --draft --notes-from-tag dist/release/*
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
|
||||||
docker-rootful:
|
|
||||||
runs-on: namespace-profile-gitea-release-docker
|
|
||||||
permissions:
|
|
||||||
packages: write # to publish to ghcr.io
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
|
||||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
|
||||||
- run: git fetch --unshallow --quiet --tags --force
|
|
||||||
- uses: docker/setup-qemu-action@v3
|
|
||||||
- uses: docker/setup-buildx-action@v3
|
|
||||||
- uses: docker/metadata-action@v5
|
|
||||||
id: meta
|
|
||||||
with:
|
|
||||||
images: |-
|
|
||||||
gitea/gitea
|
|
||||||
ghcr.io/go-gitea/gitea
|
|
||||||
flavor: |
|
|
||||||
latest=false
|
|
||||||
# 1.2.3-rc0
|
|
||||||
tags: |
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
- name: Login to GHCR using PAT
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: build rootful docker image
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
docker-rootless:
|
|
||||||
runs-on: namespace-profile-gitea-release-docker
|
|
||||||
permissions:
|
|
||||||
packages: write # to publish to ghcr.io
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
|
||||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
|
||||||
- run: git fetch --unshallow --quiet --tags --force
|
|
||||||
- uses: docker/setup-qemu-action@v3
|
|
||||||
- uses: docker/setup-buildx-action@v3
|
|
||||||
- uses: docker/metadata-action@v5
|
|
||||||
id: meta
|
|
||||||
with:
|
|
||||||
images: |-
|
|
||||||
gitea/gitea
|
|
||||||
ghcr.io/go-gitea/gitea
|
|
||||||
# each tag below will have the suffix of -rootless
|
|
||||||
flavor: |
|
|
||||||
latest=false
|
|
||||||
suffix=-rootless
|
|
||||||
# 1.2.3-rc0
|
|
||||||
tags: |
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
- name: Login to GHCR using PAT
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: build rootless docker image
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
|
||||||
push: true
|
|
||||||
file: Dockerfile.rootless
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
165
.github/workflows/release-tag-version.yml
vendored
165
.github/workflows/release-tag-version.yml
vendored
@@ -1,165 +0,0 @@
|
|||||||
name: release-tag-version
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v1.*"
|
|
||||||
- "!v1*-rc*"
|
|
||||||
- "!v1*-dev"
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
binary:
|
|
||||||
runs-on: namespace-profile-gitea-release-binary
|
|
||||||
permissions:
|
|
||||||
packages: write # to publish to ghcr.io
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
|
||||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
|
||||||
- run: git fetch --unshallow --quiet --tags --force
|
|
||||||
- uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
check-latest: true
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 22
|
|
||||||
cache: npm
|
|
||||||
cache-dependency-path: package-lock.json
|
|
||||||
- run: make deps-frontend deps-backend
|
|
||||||
# xgo build
|
|
||||||
- run: make release
|
|
||||||
env:
|
|
||||||
TAGS: bindata sqlite sqlite_unlock_notify
|
|
||||||
- name: import gpg key
|
|
||||||
id: import_gpg
|
|
||||||
uses: crazy-max/ghaction-import-gpg@v6
|
|
||||||
with:
|
|
||||||
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
|
|
||||||
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
|
|
||||||
- name: sign binaries
|
|
||||||
run: |
|
|
||||||
for f in dist/release/*; do
|
|
||||||
echo '${{ secrets.GPGSIGN_PASSPHRASE }}' | gpg --pinentry-mode loopback --passphrase-fd 0 --batch --yes --detach-sign -u ${{ steps.import_gpg.outputs.fingerprint }} --output "$f.asc" "$f"
|
|
||||||
done
|
|
||||||
# clean branch name to get the folder name in S3
|
|
||||||
- name: Get cleaned branch name
|
|
||||||
id: clean_name
|
|
||||||
run: |
|
|
||||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\/v//' -e 's/release\/v//')
|
|
||||||
echo "Cleaned name is ${REF_NAME}"
|
|
||||||
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
|
||||||
- name: configure aws
|
|
||||||
uses: aws-actions/configure-aws-credentials@v4
|
|
||||||
with:
|
|
||||||
aws-region: ${{ secrets.AWS_REGION }}
|
|
||||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
||||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
||||||
- name: upload binaries to s3
|
|
||||||
run: |
|
|
||||||
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
|
|
||||||
- name: Install GH CLI
|
|
||||||
uses: dev-hanz-ops/install-gh-cli-action@v0.1.0
|
|
||||||
with:
|
|
||||||
gh-cli-version: 2.39.1
|
|
||||||
- name: create github release
|
|
||||||
run: |
|
|
||||||
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --notes-from-tag dist/release/*
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
|
||||||
docker-rootful:
|
|
||||||
runs-on: namespace-profile-gitea-release-docker
|
|
||||||
permissions:
|
|
||||||
packages: write # to publish to ghcr.io
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
|
||||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
|
||||||
- run: git fetch --unshallow --quiet --tags --force
|
|
||||||
- uses: docker/setup-qemu-action@v3
|
|
||||||
- uses: docker/setup-buildx-action@v3
|
|
||||||
- uses: docker/metadata-action@v5
|
|
||||||
id: meta
|
|
||||||
with:
|
|
||||||
images: |-
|
|
||||||
gitea/gitea
|
|
||||||
ghcr.io/go-gitea/gitea
|
|
||||||
# this will generate tags in the following format:
|
|
||||||
# latest
|
|
||||||
# 1
|
|
||||||
# 1.2
|
|
||||||
# 1.2.3
|
|
||||||
tags: |
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
- name: Login to GHCR using PAT
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: build rootful docker image
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
docker-rootless:
|
|
||||||
runs-on: namespace-profile-gitea-release-docker
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
|
||||||
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
|
|
||||||
- run: git fetch --unshallow --quiet --tags --force
|
|
||||||
- uses: docker/setup-qemu-action@v3
|
|
||||||
- uses: docker/setup-buildx-action@v3
|
|
||||||
- uses: docker/metadata-action@v5
|
|
||||||
id: meta
|
|
||||||
with:
|
|
||||||
images: |-
|
|
||||||
gitea/gitea
|
|
||||||
ghcr.io/go-gitea/gitea
|
|
||||||
# each tag below will have the suffix of -rootless
|
|
||||||
flavor: |
|
|
||||||
suffix=-rootless,onlatest=true
|
|
||||||
# this will generate tags in the following format (with -rootless suffix added):
|
|
||||||
# latest
|
|
||||||
# 1
|
|
||||||
# 1.2
|
|
||||||
# 1.2.3
|
|
||||||
tags: |
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
- name: Login to Docker Hub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
- name: Login to GHCR using PAT
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.repository_owner }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: build rootless docker image
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
platforms: linux/amd64,linux/arm64,linux/riscv64
|
|
||||||
push: true
|
|
||||||
file: Dockerfile.rootless
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
39
.gitignore
vendored
39
.gitignore
vendored
@@ -9,18 +9,12 @@ _test
|
|||||||
|
|
||||||
# IntelliJ
|
# IntelliJ
|
||||||
.idea
|
.idea
|
||||||
.run
|
|
||||||
|
|
||||||
# IntelliJ Gateway
|
|
||||||
.uuid
|
|
||||||
|
|
||||||
# Goland's output filename can not be set manually
|
# Goland's output filename can not be set manually
|
||||||
/go_build_*
|
/go_build_*
|
||||||
/gitea_*
|
|
||||||
|
|
||||||
# MS VSCode
|
# MS VSCode
|
||||||
.vscode
|
.vscode
|
||||||
__debug_bin*
|
__debug_bin
|
||||||
|
|
||||||
*.cgo1.go
|
*.cgo1.go
|
||||||
*.cgo2.c
|
*.cgo2.c
|
||||||
@@ -33,7 +27,6 @@ _testmain.go
|
|||||||
*.exe
|
*.exe
|
||||||
*.test
|
*.test
|
||||||
*.prof
|
*.prof
|
||||||
*.tsbuildinfo
|
|
||||||
|
|
||||||
*coverage.out
|
*coverage.out
|
||||||
coverage.all
|
coverage.all
|
||||||
@@ -64,7 +57,7 @@ cpu.out
|
|||||||
/data
|
/data
|
||||||
/indexers
|
/indexers
|
||||||
/log
|
/log
|
||||||
/public/assets/img/avatar
|
/public/img/avatar
|
||||||
/tests/integration/gitea-integration-*
|
/tests/integration/gitea-integration-*
|
||||||
/tests/integration/indexers-*
|
/tests/integration/indexers-*
|
||||||
/tests/e2e/gitea-e2e-*
|
/tests/e2e/gitea-e2e-*
|
||||||
@@ -75,24 +68,31 @@ cpu.out
|
|||||||
/tests/*.ini
|
/tests/*.ini
|
||||||
/tests/**/*.git/**/*.sample
|
/tests/**/*.git/**/*.sample
|
||||||
/node_modules
|
/node_modules
|
||||||
/.venv
|
|
||||||
/yarn.lock
|
/yarn.lock
|
||||||
/yarn-error.log
|
/yarn-error.log
|
||||||
/npm-debug.log*
|
/npm-debug.log*
|
||||||
/public/assets/js
|
/public/js
|
||||||
/public/assets/css
|
/public/css
|
||||||
/public/assets/fonts
|
/public/fonts
|
||||||
/public/assets/licenses.txt
|
/public/img/webpack
|
||||||
/vendor
|
/vendor
|
||||||
|
/web_src/fomantic/node_modules
|
||||||
|
/web_src/fomantic/build/*
|
||||||
|
!/web_src/fomantic/build/semantic.js
|
||||||
|
!/web_src/fomantic/build/semantic.css
|
||||||
|
!/web_src/fomantic/build/themes
|
||||||
|
/web_src/fomantic/build/themes/*
|
||||||
|
!/web_src/fomantic/build/themes/default
|
||||||
|
/web_src/fomantic/build/themes/default/assets/*
|
||||||
|
!/web_src/fomantic/build/themes/default/assets/fonts
|
||||||
|
/web_src/fomantic/build/themes/default/assets/fonts/*
|
||||||
|
!/web_src/fomantic/build/themes/default/assets/fonts/icons.woff2
|
||||||
|
!/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
|
||||||
/VERSION
|
/VERSION
|
||||||
/.air
|
/.air
|
||||||
/.go-licenses
|
/.go-licenses
|
||||||
|
|
||||||
# Files and folders that were previously generated
|
|
||||||
/public/assets/img/webpack
|
|
||||||
|
|
||||||
# Snapcraft
|
# Snapcraft
|
||||||
/gitea_a*.txt
|
|
||||||
snap/.snapcraft/
|
snap/.snapcraft/
|
||||||
parts/
|
parts/
|
||||||
stage/
|
stage/
|
||||||
@@ -102,9 +102,6 @@ prime/
|
|||||||
*_source.tar.bz2
|
*_source.tar.bz2
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
# nix-direnv generated files
|
|
||||||
.direnv/
|
|
||||||
|
|
||||||
# Make evidence files
|
# Make evidence files
|
||||||
/.make_evidence
|
/.make_evidence
|
||||||
|
|
||||||
|
|||||||
20
.gitpod.yml
20
.gitpod.yml
@@ -10,19 +10,10 @@ tasks:
|
|||||||
- name: Run backend
|
- name: Run backend
|
||||||
command: |
|
command: |
|
||||||
gp sync-await setup
|
gp sync-await setup
|
||||||
|
if [ ! -f custom/conf/app.ini ]
|
||||||
# Get the URL and extract the domain
|
then
|
||||||
url=$(gp url 3000)
|
|
||||||
domain=$(echo $url | awk -F[/:] '{print $4}')
|
|
||||||
|
|
||||||
if [ -f custom/conf/app.ini ]; then
|
|
||||||
sed -i "s|^ROOT_URL =.*|ROOT_URL = ${url}/|" custom/conf/app.ini
|
|
||||||
sed -i "s|^DOMAIN =.*|DOMAIN = ${domain}|" custom/conf/app.ini
|
|
||||||
sed -i "s|^SSH_DOMAIN =.*|SSH_DOMAIN = ${domain}|" custom/conf/app.ini
|
|
||||||
sed -i "s|^NO_REPLY_ADDRESS =.*|SSH_DOMAIN = noreply.${domain}|" custom/conf/app.ini
|
|
||||||
else
|
|
||||||
mkdir -p custom/conf/
|
mkdir -p custom/conf/
|
||||||
echo -e "[server]\nROOT_URL = ${url}/" > custom/conf/app.ini
|
echo -e "[server]\nROOT_URL=$(gp url 3000)/" > custom/conf/app.ini
|
||||||
echo -e "\n[database]\nDB_TYPE = sqlite3\nPATH = $GITPOD_REPO_ROOT/data/gitea.db" >> custom/conf/app.ini
|
echo -e "\n[database]\nDB_TYPE = sqlite3\nPATH = $GITPOD_REPO_ROOT/data/gitea.db" >> custom/conf/app.ini
|
||||||
fi
|
fi
|
||||||
export TAGS="sqlite sqlite_unlock_notify"
|
export TAGS="sqlite sqlite_unlock_notify"
|
||||||
@@ -42,9 +33,8 @@ vscode:
|
|||||||
- DavidAnson.vscode-markdownlint
|
- DavidAnson.vscode-markdownlint
|
||||||
- Vue.volar
|
- Vue.volar
|
||||||
- ms-azuretools.vscode-docker
|
- ms-azuretools.vscode-docker
|
||||||
- vitest.explorer
|
- zixuanchen.vitest-explorer
|
||||||
- cweijan.vscode-database-client2
|
- alexcvzz.vscode-sqlite
|
||||||
- GitHub.vscode-pull-request-github
|
|
||||||
|
|
||||||
ports:
|
ports:
|
||||||
- name: Gitea
|
- name: Gitea
|
||||||
|
|||||||
261
.golangci.yml
261
.golangci.yml
@@ -1,172 +1,131 @@
|
|||||||
version: "2"
|
|
||||||
output:
|
|
||||||
sort-order:
|
|
||||||
- file
|
|
||||||
linters:
|
linters:
|
||||||
default: none
|
|
||||||
enable:
|
enable:
|
||||||
- bidichk
|
- bidichk
|
||||||
|
# - deadcode # deprecated - https://github.com/golangci/golangci-lint/issues/1841
|
||||||
- depguard
|
- depguard
|
||||||
- dupl
|
- dupl
|
||||||
- errcheck
|
- errcheck
|
||||||
- forbidigo
|
- forbidigo
|
||||||
- gocritic
|
- gocritic
|
||||||
|
# - gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
|
||||||
|
- gofmt
|
||||||
|
- gofumpt
|
||||||
|
- gosimple
|
||||||
- govet
|
- govet
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- mirror
|
|
||||||
- nakedret
|
- nakedret
|
||||||
- nolintlint
|
- nolintlint
|
||||||
- perfsprint
|
|
||||||
- revive
|
- revive
|
||||||
- staticcheck
|
- staticcheck
|
||||||
- testifylint
|
# - structcheck # deprecated - https://github.com/golangci/golangci-lint/issues/1841
|
||||||
|
- stylecheck
|
||||||
|
- typecheck
|
||||||
- unconvert
|
- unconvert
|
||||||
- unparam
|
|
||||||
- unused
|
- unused
|
||||||
- usestdlibvars
|
# - varcheck # deprecated - https://github.com/golangci/golangci-lint/issues/1841
|
||||||
- usetesting
|
|
||||||
- wastedassign
|
- wastedassign
|
||||||
settings:
|
enable-all: false
|
||||||
depguard:
|
disable-all: true
|
||||||
rules:
|
fast: false
|
||||||
main:
|
|
||||||
deny:
|
run:
|
||||||
- pkg: encoding/json
|
go: "1.20"
|
||||||
desc: use gitea's modules/json instead of encoding/json
|
timeout: 10m
|
||||||
- pkg: github.com/unknwon/com
|
skip-dirs:
|
||||||
desc: use gitea's util and replacements
|
- node_modules
|
||||||
- pkg: io/ioutil
|
- public
|
||||||
desc: use os or io instead
|
- web_src
|
||||||
- pkg: golang.org/x/exp
|
|
||||||
desc: it's experimental and unreliable
|
linters-settings:
|
||||||
- pkg: code.gitea.io/gitea/modules/git/internal
|
stylecheck:
|
||||||
desc: do not use the internal package, use AddXxx function instead
|
checks: ["all", "-ST1005", "-ST1003"]
|
||||||
- pkg: gopkg.in/ini.v1
|
nakedret:
|
||||||
desc: do not use the ini package, use gitea's config system instead
|
max-func-lines: 0
|
||||||
- pkg: gitea.com/go-chi/cache
|
gocritic:
|
||||||
desc: do not use the go-chi cache package, use gitea's cache system
|
disabled-checks:
|
||||||
gocritic:
|
- ifElseChain
|
||||||
disabled-checks:
|
- singleCaseSwitch # Every time this occurred in the code, there was no other way.
|
||||||
- ifElseChain
|
revive:
|
||||||
- singleCaseSwitch # Every time this occurred in the code, there was no other way.
|
ignore-generated-header: false
|
||||||
revive:
|
severity: warning
|
||||||
severity: error
|
confidence: 0.8
|
||||||
rules:
|
errorCode: 1
|
||||||
- name: atomic
|
warningCode: 1
|
||||||
- name: bare-return
|
|
||||||
- name: blank-imports
|
|
||||||
- name: constant-logical-expr
|
|
||||||
- name: context-as-argument
|
|
||||||
- name: context-keys-type
|
|
||||||
- name: dot-imports
|
|
||||||
- name: duplicated-imports
|
|
||||||
- name: empty-lines
|
|
||||||
- name: error-naming
|
|
||||||
- name: error-return
|
|
||||||
- name: error-strings
|
|
||||||
- name: errorf
|
|
||||||
- name: exported
|
|
||||||
- name: identical-branches
|
|
||||||
- name: if-return
|
|
||||||
- name: increment-decrement
|
|
||||||
- name: indent-error-flow
|
|
||||||
- name: modifies-value-receiver
|
|
||||||
- name: package-comments
|
|
||||||
- name: range
|
|
||||||
- name: receiver-naming
|
|
||||||
- name: redefines-builtin-id
|
|
||||||
- name: string-of-int
|
|
||||||
- name: superfluous-else
|
|
||||||
- name: time-naming
|
|
||||||
- name: unconditional-recursion
|
|
||||||
- name: unexported-return
|
|
||||||
- name: unreachable-code
|
|
||||||
- name: var-declaration
|
|
||||||
- name: var-naming
|
|
||||||
staticcheck:
|
|
||||||
checks:
|
|
||||||
- all
|
|
||||||
- -ST1003
|
|
||||||
- -ST1005
|
|
||||||
- -QF1001
|
|
||||||
- -QF1006
|
|
||||||
- -QF1008
|
|
||||||
testifylint:
|
|
||||||
disable:
|
|
||||||
- go-require
|
|
||||||
- require-error
|
|
||||||
usetesting:
|
|
||||||
os-temp-dir: true
|
|
||||||
exclusions:
|
|
||||||
generated: lax
|
|
||||||
presets:
|
|
||||||
- comments
|
|
||||||
- common-false-positives
|
|
||||||
- legacy
|
|
||||||
- std-error-handling
|
|
||||||
rules:
|
rules:
|
||||||
- linters:
|
- name: blank-imports
|
||||||
- dupl
|
- name: context-as-argument
|
||||||
- errcheck
|
- name: context-keys-type
|
||||||
- gocyclo
|
- name: dot-imports
|
||||||
- gosec
|
- name: error-return
|
||||||
- staticcheck
|
- name: error-strings
|
||||||
- unparam
|
- name: error-naming
|
||||||
path: _test\.go
|
- name: exported
|
||||||
- linters:
|
- name: if-return
|
||||||
- dupl
|
- name: increment-decrement
|
||||||
- errcheck
|
- name: var-naming
|
||||||
- gocyclo
|
- name: var-declaration
|
||||||
- gosec
|
- name: package-comments
|
||||||
path: models/migrations/v
|
- name: range
|
||||||
- linters:
|
- name: receiver-naming
|
||||||
- forbidigo
|
- name: time-naming
|
||||||
path: cmd
|
- name: unexported-return
|
||||||
- linters:
|
- name: indent-error-flow
|
||||||
- dupl
|
- name: errorf
|
||||||
text: (?i)webhook
|
- name: duplicated-imports
|
||||||
- linters:
|
- name: modifies-value-receiver
|
||||||
- gocritic
|
gofumpt:
|
||||||
text: (?i)`ID' should not be capitalized
|
extra-rules: true
|
||||||
- linters:
|
lang-version: "1.20"
|
||||||
- deadcode
|
depguard:
|
||||||
- unused
|
list-type: denylist
|
||||||
text: (?i)swagger
|
# Check the list against standard lib.
|
||||||
- linters:
|
include-go-root: true
|
||||||
- staticcheck
|
packages-with-error-message:
|
||||||
text: (?i)argument x is overwritten before first use
|
- encoding/json: "use gitea's modules/json instead of encoding/json"
|
||||||
- linters:
|
- github.com/unknwon/com: "use gitea's util and replacements"
|
||||||
- gocritic
|
- io/ioutil: "use os or io instead"
|
||||||
text: '(?i)commentFormatting: put a space between `//` and comment text'
|
- golang.org/x/exp: "it's experimental and unreliable."
|
||||||
- linters:
|
- code.gitea.io/gitea/modules/git/internal: "do not use the internal package, use AddXxx function instead"
|
||||||
- gocritic
|
- gopkg.in/ini.v1: "do not use the ini package, use gitea's config system instead"
|
||||||
text: '(?i)exitAfterDefer:'
|
|
||||||
paths:
|
|
||||||
- node_modules
|
|
||||||
- public
|
|
||||||
- web_src
|
|
||||||
- third_party$
|
|
||||||
- builtin$
|
|
||||||
- examples$
|
|
||||||
issues:
|
issues:
|
||||||
max-issues-per-linter: 0
|
max-issues-per-linter: 0
|
||||||
max-same-issues: 0
|
max-same-issues: 0
|
||||||
formatters:
|
exclude-rules:
|
||||||
enable:
|
# Exclude some linters from running on tests files.
|
||||||
- gofmt
|
- path: _test\.go
|
||||||
- gofumpt
|
linters:
|
||||||
settings:
|
- gocyclo
|
||||||
gofumpt:
|
- errcheck
|
||||||
extra-rules: true
|
- dupl
|
||||||
exclusions:
|
- gosec
|
||||||
generated: lax
|
- unparam
|
||||||
paths:
|
- staticcheck
|
||||||
- node_modules
|
- path: models/migrations/v
|
||||||
- public
|
linters:
|
||||||
- web_src
|
- gocyclo
|
||||||
- third_party$
|
- errcheck
|
||||||
- builtin$
|
- dupl
|
||||||
- examples$
|
- gosec
|
||||||
|
- path: cmd
|
||||||
run:
|
linters:
|
||||||
timeout: 10m
|
- forbidigo
|
||||||
|
- linters:
|
||||||
|
- dupl
|
||||||
|
text: "webhook"
|
||||||
|
- linters:
|
||||||
|
- gocritic
|
||||||
|
text: "`ID' should not be capitalized"
|
||||||
|
- linters:
|
||||||
|
- unused
|
||||||
|
- deadcode
|
||||||
|
text: "swagger"
|
||||||
|
- linters:
|
||||||
|
- staticcheck
|
||||||
|
text: "argument x is overwritten before first use"
|
||||||
|
- text: "commentFormatting: put a space between `//` and comment text"
|
||||||
|
linters:
|
||||||
|
- gocritic
|
||||||
|
- text: "exitAfterDefer:"
|
||||||
|
linters:
|
||||||
|
- gocritic
|
||||||
|
|||||||
3
.ignore
3
.ignore
@@ -4,8 +4,5 @@
|
|||||||
/modules/options/bindata.go
|
/modules/options/bindata.go
|
||||||
/modules/public/bindata.go
|
/modules/public/bindata.go
|
||||||
/modules/templates/bindata.go
|
/modules/templates/bindata.go
|
||||||
/options/gitignore
|
|
||||||
/options/license
|
|
||||||
/public/assets
|
|
||||||
/vendor
|
/vendor
|
||||||
node_modules
|
node_modules
|
||||||
|
|||||||
2
.mailmap
2
.mailmap
@@ -1,2 +0,0 @@
|
|||||||
Unknwon <u@gogs.io> <joe2010xtmf@163.com>
|
|
||||||
Unknwon <u@gogs.io> 无闻 <u@gogs.io>
|
|
||||||
@@ -1,15 +1,18 @@
|
|||||||
commands-show-output: false
|
commands-show-output: false
|
||||||
fenced-code-language: false
|
fenced-code-language: false
|
||||||
first-line-h1: false
|
first-line-h1: false
|
||||||
heading-increment: false
|
header-increment: false
|
||||||
line-length: {code_blocks: false, tables: false, stern: true, line_length: -1}
|
line-length: {code_blocks: false, tables: false, stern: true, line_length: -1}
|
||||||
no-alt-text: false
|
no-alt-text: false
|
||||||
no-bare-urls: false
|
no-bare-urls: false
|
||||||
no-emphasis-as-heading: false
|
no-blanks-blockquote: false
|
||||||
|
no-duplicate-header: {allow_different_nesting: true}
|
||||||
|
no-emphasis-as-header: false
|
||||||
no-empty-links: false
|
no-empty-links: false
|
||||||
no-hard-tabs: {code_blocks: false}
|
no-hard-tabs: {code_blocks: false}
|
||||||
no-inline-html: false
|
no-inline-html: false
|
||||||
no-space-in-code: false
|
no-space-in-code: false
|
||||||
no-space-in-emphasis: false
|
no-space-in-emphasis: false
|
||||||
|
no-trailing-punctuation: false
|
||||||
no-trailing-spaces: {br_spaces: 0}
|
no-trailing-spaces: {br_spaces: 0}
|
||||||
single-h1: false
|
single-h1: false
|
||||||
|
|||||||
141
.stylelintrc.yaml
Normal file
141
.stylelintrc.yaml
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
plugins:
|
||||||
|
- stylelint-declaration-strict-value
|
||||||
|
|
||||||
|
ignoreFiles:
|
||||||
|
- "**/*.go"
|
||||||
|
|
||||||
|
overrides:
|
||||||
|
- files: ["**/chroma/*", "**/codemirror/*", "**/standalone/*", "**/console.css", "font_i18n.css"]
|
||||||
|
rules:
|
||||||
|
scale-unlimited/declaration-strict-value: null
|
||||||
|
- files: ["**/chroma/*", "**/codemirror/*"]
|
||||||
|
rules:
|
||||||
|
block-no-empty: null
|
||||||
|
- files: ["**/*.vue"]
|
||||||
|
customSyntax: postcss-html
|
||||||
|
|
||||||
|
rules:
|
||||||
|
alpha-value-notation: null
|
||||||
|
annotation-no-unknown: true
|
||||||
|
at-rule-allowed-list: null
|
||||||
|
at-rule-disallowed-list: null
|
||||||
|
at-rule-empty-line-before: null
|
||||||
|
at-rule-no-unknown: true
|
||||||
|
at-rule-no-vendor-prefix: true
|
||||||
|
at-rule-property-required-list: null
|
||||||
|
block-no-empty: true
|
||||||
|
color-function-notation: null
|
||||||
|
color-hex-alpha: null
|
||||||
|
color-hex-length: null
|
||||||
|
color-named: null
|
||||||
|
color-no-hex: null
|
||||||
|
color-no-invalid-hex: true
|
||||||
|
comment-empty-line-before: null
|
||||||
|
comment-no-empty: true
|
||||||
|
comment-pattern: null
|
||||||
|
comment-whitespace-inside: null
|
||||||
|
comment-word-disallowed-list: null
|
||||||
|
custom-media-pattern: null
|
||||||
|
custom-property-empty-line-before: null
|
||||||
|
custom-property-no-missing-var-function: true
|
||||||
|
custom-property-pattern: null
|
||||||
|
declaration-block-no-duplicate-custom-properties: true
|
||||||
|
declaration-block-no-duplicate-properties: [true, {ignore: [consecutive-duplicates-with-different-values]}]
|
||||||
|
declaration-block-no-redundant-longhand-properties: null
|
||||||
|
declaration-block-no-shorthand-property-overrides: null
|
||||||
|
declaration-block-single-line-max-declarations: null
|
||||||
|
declaration-empty-line-before: null
|
||||||
|
declaration-no-important: null
|
||||||
|
declaration-property-max-values: null
|
||||||
|
declaration-property-unit-allowed-list: null
|
||||||
|
declaration-property-unit-disallowed-list: null
|
||||||
|
declaration-property-value-allowed-list: null
|
||||||
|
declaration-property-value-disallowed-list: null
|
||||||
|
declaration-property-value-no-unknown: true
|
||||||
|
font-family-name-quotes: always-where-recommended
|
||||||
|
font-family-no-duplicate-names: true
|
||||||
|
font-family-no-missing-generic-family-keyword: true
|
||||||
|
font-weight-notation: null
|
||||||
|
function-allowed-list: null
|
||||||
|
function-calc-no-unspaced-operator: true
|
||||||
|
function-disallowed-list: null
|
||||||
|
function-linear-gradient-no-nonstandard-direction: true
|
||||||
|
function-name-case: lower
|
||||||
|
function-no-unknown: null
|
||||||
|
function-url-no-scheme-relative: null
|
||||||
|
function-url-quotes: always
|
||||||
|
function-url-scheme-allowed-list: null
|
||||||
|
function-url-scheme-disallowed-list: null
|
||||||
|
hue-degree-notation: null
|
||||||
|
import-notation: string
|
||||||
|
keyframe-block-no-duplicate-selectors: true
|
||||||
|
keyframe-declaration-no-important: true
|
||||||
|
keyframe-selector-notation: null
|
||||||
|
keyframes-name-pattern: null
|
||||||
|
length-zero-no-unit: true
|
||||||
|
max-nesting-depth: null
|
||||||
|
media-feature-name-allowed-list: null
|
||||||
|
media-feature-name-disallowed-list: null
|
||||||
|
media-feature-name-no-unknown: true
|
||||||
|
media-feature-name-no-vendor-prefix: true
|
||||||
|
media-feature-name-unit-allowed-list: null
|
||||||
|
media-feature-name-value-allowed-list: null
|
||||||
|
media-feature-name-value-no-unknown: true
|
||||||
|
media-feature-range-notation: null
|
||||||
|
named-grid-areas-no-invalid: true
|
||||||
|
no-descending-specificity: null
|
||||||
|
no-duplicate-at-import-rules: true
|
||||||
|
no-duplicate-selectors: true
|
||||||
|
no-empty-source: true
|
||||||
|
no-invalid-double-slash-comments: true
|
||||||
|
no-invalid-position-at-import-rule: null
|
||||||
|
no-irregular-whitespace: true
|
||||||
|
no-unknown-animations: null
|
||||||
|
no-unknown-custom-properties: null
|
||||||
|
number-max-precision: null
|
||||||
|
property-allowed-list: null
|
||||||
|
property-disallowed-list: null
|
||||||
|
property-no-unknown: true
|
||||||
|
property-no-vendor-prefix: null
|
||||||
|
rule-empty-line-before: null
|
||||||
|
rule-selector-property-disallowed-list: null
|
||||||
|
scale-unlimited/declaration-strict-value: [[color, background-color, border-color, font-weight], {ignoreValues: /^(inherit|transparent|unset|initial|currentcolor|none)$/, ignoreFunctions: false, disableFix: true}]
|
||||||
|
selector-attribute-name-disallowed-list: null
|
||||||
|
selector-attribute-operator-allowed-list: null
|
||||||
|
selector-attribute-operator-disallowed-list: null
|
||||||
|
selector-attribute-quotes: always
|
||||||
|
selector-class-pattern: null
|
||||||
|
selector-combinator-allowed-list: null
|
||||||
|
selector-combinator-disallowed-list: null
|
||||||
|
selector-disallowed-list: null
|
||||||
|
selector-id-pattern: null
|
||||||
|
selector-max-attribute: null
|
||||||
|
selector-max-class: null
|
||||||
|
selector-max-combinators: null
|
||||||
|
selector-max-compound-selectors: null
|
||||||
|
selector-max-id: null
|
||||||
|
selector-max-pseudo-class: null
|
||||||
|
selector-max-specificity: null
|
||||||
|
selector-max-type: null
|
||||||
|
selector-max-universal: null
|
||||||
|
selector-nested-pattern: null
|
||||||
|
selector-no-qualifying-type: null
|
||||||
|
selector-no-vendor-prefix: true
|
||||||
|
selector-not-notation: null
|
||||||
|
selector-pseudo-class-allowed-list: null
|
||||||
|
selector-pseudo-class-disallowed-list: null
|
||||||
|
selector-pseudo-class-no-unknown: true
|
||||||
|
selector-pseudo-element-allowed-list: null
|
||||||
|
selector-pseudo-element-colon-notation: double
|
||||||
|
selector-pseudo-element-disallowed-list: null
|
||||||
|
selector-pseudo-element-no-unknown: true
|
||||||
|
selector-type-case: lower
|
||||||
|
selector-type-no-unknown: [true, {ignore: [custom-elements]}]
|
||||||
|
shorthand-property-no-redundant-values: true
|
||||||
|
string-no-newline: true
|
||||||
|
time-min-milliseconds: null
|
||||||
|
unit-allowed-list: null
|
||||||
|
unit-disallowed-list: null
|
||||||
|
unit-no-unknown: true
|
||||||
|
value-keyword-case: null
|
||||||
|
value-no-vendor-prefix: [true, {ignoreValues: [box, inline-box]}]
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
extends: default
|
|
||||||
|
|
||||||
rules:
|
|
||||||
braces:
|
|
||||||
min-spaces-inside: 0
|
|
||||||
max-spaces-inside: 1
|
|
||||||
min-spaces-inside-empty: 0
|
|
||||||
max-spaces-inside-empty: 0
|
|
||||||
|
|
||||||
brackets:
|
|
||||||
min-spaces-inside: 0
|
|
||||||
max-spaces-inside: 1
|
|
||||||
min-spaces-inside-empty: 0
|
|
||||||
max-spaces-inside-empty: 0
|
|
||||||
|
|
||||||
comments:
|
|
||||||
require-starting-space: true
|
|
||||||
ignore-shebangs: true
|
|
||||||
min-spaces-from-content: 1
|
|
||||||
|
|
||||||
comments-indentation:
|
|
||||||
level: error
|
|
||||||
|
|
||||||
document-start:
|
|
||||||
level: error
|
|
||||||
present: false
|
|
||||||
|
|
||||||
document-end:
|
|
||||||
present: false
|
|
||||||
|
|
||||||
empty-lines:
|
|
||||||
max: 1
|
|
||||||
|
|
||||||
indentation:
|
|
||||||
spaces: 2
|
|
||||||
|
|
||||||
line-length: disable
|
|
||||||
|
|
||||||
truthy:
|
|
||||||
allowed-values: ["true", "false", "on", "off"]
|
|
||||||
|
|
||||||
ignore: |
|
|
||||||
.venv
|
|
||||||
node_modules
|
|
||||||
@@ -42,13 +42,13 @@ GARGS = "--no-print-directory"
|
|||||||
|
|
||||||
# The GNU convention is to use the lowercased `prefix` variable/macro to
|
# The GNU convention is to use the lowercased `prefix` variable/macro to
|
||||||
# specify the installation directory. Humor them.
|
# specify the installation directory. Humor them.
|
||||||
GPREFIX =
|
GPREFIX = ""
|
||||||
.if defined(PREFIX) && ! defined(prefix)
|
.if defined(PREFIX) && ! defined(prefix)
|
||||||
GPREFIX = 'prefix = "$(PREFIX)"'
|
GPREFIX = 'prefix = "$(PREFIX)"'
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
.BEGIN: .SILENT
|
.BEGIN: .SILENT
|
||||||
which $(GMAKE) || (printf "Error: GNU Make is required!\n\n" 1>&2 && false)
|
which $(GMAKE) || printf "Error: GNU Make is required!\n\n" 1>&2 && false
|
||||||
|
|
||||||
.PHONY: FRC
|
.PHONY: FRC
|
||||||
$(.TARGETS): FRC
|
$(.TARGETS): FRC
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
7705
CHANGELOG.md
7705
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
107
CONTRIBUTING.md
107
CONTRIBUTING.md
@@ -8,7 +8,6 @@
|
|||||||
- [How to report issues](#how-to-report-issues)
|
- [How to report issues](#how-to-report-issues)
|
||||||
- [Types of issues](#types-of-issues)
|
- [Types of issues](#types-of-issues)
|
||||||
- [Discuss your design before the implementation](#discuss-your-design-before-the-implementation)
|
- [Discuss your design before the implementation](#discuss-your-design-before-the-implementation)
|
||||||
- [Issue locking](#issue-locking)
|
|
||||||
- [Building Gitea](#building-gitea)
|
- [Building Gitea](#building-gitea)
|
||||||
- [Dependencies](#dependencies)
|
- [Dependencies](#dependencies)
|
||||||
- [Backend](#backend)
|
- [Backend](#backend)
|
||||||
@@ -48,7 +47,6 @@
|
|||||||
- [Release Cycle](#release-cycle)
|
- [Release Cycle](#release-cycle)
|
||||||
- [Maintainers](#maintainers)
|
- [Maintainers](#maintainers)
|
||||||
- [Technical Oversight Committee (TOC)](#technical-oversight-committee-toc)
|
- [Technical Oversight Committee (TOC)](#technical-oversight-committee-toc)
|
||||||
- [TOC election process](#toc-election-process)
|
|
||||||
- [Current TOC members](#current-toc-members)
|
- [Current TOC members](#current-toc-members)
|
||||||
- [Previous TOC/owners members](#previous-tocowners-members)
|
- [Previous TOC/owners members](#previous-tocowners-members)
|
||||||
- [Governance Compensation](#governance-compensation)
|
- [Governance Compensation](#governance-compensation)
|
||||||
@@ -62,7 +60,7 @@
|
|||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
This document explains how to contribute changes to the Gitea project. \
|
This document explains how to contribute changes to the Gitea project. \
|
||||||
It assumes you have followed the [installation instructions](https://docs.gitea.com/category/installation). \
|
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).
|
Sensitive security-related issues should be reported to [security@gitea.io](mailto:security@gitea.io).
|
||||||
|
|
||||||
For configuring IDEs for Gitea development, see the [contributed IDE configurations](contrib/ide/).
|
For configuring IDEs for Gitea development, see the [contributed IDE configurations](contrib/ide/).
|
||||||
@@ -77,7 +75,7 @@ If your issue has not been reported yet, [open an issue](https://github.com/go-g
|
|||||||
and answer the questions so we can understand and reproduce the problematic behavior. \
|
and answer the questions so we can understand and reproduce the problematic behavior. \
|
||||||
Please write clear and concise instructions so that we can reproduce the behavior — even if it seems obvious. \
|
Please write clear and concise instructions so that we can reproduce the behavior — even if it seems obvious. \
|
||||||
The more detailed and specific you are, the faster we can fix the issue. \
|
The more detailed and specific you are, the faster we can fix the issue. \
|
||||||
It is really helpful if you can reproduce your problem on a site running on the latest commits, i.e. <https://demo.gitea.com>, as perhaps your problem has already been fixed on a current version. \
|
It is really helpful if you can reproduce your problem on a site running on the latest commits, i.e. <https://try.gitea.io>, as perhaps your problem has already been fixed on a current version. \
|
||||||
Please follow the guidelines described in [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) for your report.
|
Please follow the guidelines described in [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html) for your report.
|
||||||
|
|
||||||
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.
|
||||||
@@ -104,13 +102,6 @@ the goals for the project and tools.
|
|||||||
|
|
||||||
Pull requests should not be the place for architecture discussions.
|
Pull requests should not be the place for architecture discussions.
|
||||||
|
|
||||||
### Issue locking
|
|
||||||
|
|
||||||
Commenting on closed or merged issues/PRs is strongly discouraged.
|
|
||||||
Such comments will likely be overlooked as some maintainers may not view notifications on closed issues, thinking that the item is resolved.
|
|
||||||
As such, commenting on closed/merged issues/PRs may be disabled prior to the scheduled auto-locking if a discussion starts or if unrelated comments are posted.
|
|
||||||
If further discussion is needed, we encourage you to open a new issue instead and we recommend linking to the issue/PR in question for context.
|
|
||||||
|
|
||||||
## Building Gitea
|
## Building Gitea
|
||||||
|
|
||||||
See the [development setup instructions](https://docs.gitea.com/development/hacking-on-gitea).
|
See the [development setup instructions](https://docs.gitea.com/development/hacking-on-gitea).
|
||||||
@@ -119,7 +110,7 @@ See the [development setup instructions](https://docs.gitea.com/development/hack
|
|||||||
|
|
||||||
### Backend
|
### Backend
|
||||||
|
|
||||||
Go dependencies are managed using [Go Modules](https://go.dev/cmd/go/#hdr-Module_maintenance). \
|
Go dependencies are managed using [Go Modules](https://golang.org/cmd/go/#hdr-Module_maintenance). \
|
||||||
You can find more details in the [go mod documentation](https://go.dev/ref/mod) and the [Go Modules Wiki](https://github.com/golang/go/wiki/Modules).
|
You can find more details in the [go mod documentation](https://go.dev/ref/mod) and the [Go Modules Wiki](https://github.com/golang/go/wiki/Modules).
|
||||||
|
|
||||||
Pull requests should only modify `go.mod` and `go.sum` where it is related to your change, be it a bugfix or a new feature. \
|
Pull requests should only modify `go.mod` and `go.sum` where it is related to your change, be it a bugfix or a new feature. \
|
||||||
@@ -176,13 +167,13 @@ Here's how to run the test suite:
|
|||||||
|
|
||||||
| Command | Action | |
|
| Command | Action | |
|
||||||
| :------------------------------------- | :----------------------------------------------- | ------------ |
|
| :------------------------------------- | :----------------------------------------------- | ------------ |
|
||||||
|``make test[\#SpecificTestName]`` | run unit test(s) | |
|
|``make test[\#SpecificTestName]`` | run unit test(s) |
|
||||||
|``make test-sqlite[\#SpecificTestName]``| run [integration](tests/integration) test(s) for SQLite |[More details](tests/integration/README.md) |
|
|``make test-sqlite[\#SpecificTestName]``| run [integration](tests/integration) test(s) for SQLite |[More details](tests/integration/README.md) |
|
||||||
|``make test-e2e-sqlite[\#SpecificTestName]``| run [end-to-end](tests/e2e) test(s) for SQLite |[More details](tests/e2e/README.md) |
|
|``make test-e2e-sqlite[\#SpecificTestName]``| run [end-to-end](tests/e2e) test(s) for SQLite |[More details](tests/e2e/README.md) |
|
||||||
|
|
||||||
## Translation
|
## Translation
|
||||||
|
|
||||||
All translation work happens on [Crowdin](https://translate.gitea.com).
|
All translation work happens on [Crowdin](https://crowdin.com/project/gitea).
|
||||||
The only translation that is maintained in this repository is [the English translation](https://github.com/go-gitea/gitea/blob/main/options/locale/locale_en-US.ini).
|
The only translation that is maintained in this repository is [the English translation](https://github.com/go-gitea/gitea/blob/main/options/locale/locale_en-US.ini).
|
||||||
It is synced regularly with Crowdin. \
|
It is synced regularly with Crowdin. \
|
||||||
Other locales on main branch **should not** be updated manually as they will be overwritten with each sync. \
|
Other locales on main branch **should not** be updated manually as they will be overwritten with each sync. \
|
||||||
@@ -212,20 +203,10 @@ Some of the key points:
|
|||||||
|
|
||||||
In the PR title, describe the problem you are fixing, not how you are fixing it. \
|
In the PR title, describe the problem you are fixing, not how you are fixing it. \
|
||||||
Use the first comment as a summary of your PR. \
|
Use the first comment as a summary of your PR. \
|
||||||
In the PR summary, you can describe exactly how you are fixing this problem.
|
In the PR summary, you can describe exactly how you are fixing this problem. \
|
||||||
|
|
||||||
Keep this summary up-to-date as the PR evolves. \
|
Keep this summary up-to-date as the PR evolves. \
|
||||||
If your PR changes the UI, you must add **after** screenshots in the PR summary. \
|
If your PR changes the UI, you must add **after** screenshots in the PR summary. \
|
||||||
If you are not implementing a new feature, you should also post **before** screenshots for comparison.
|
If you are not implementing a new feature, you should also post **before** screenshots for comparison. \
|
||||||
|
|
||||||
If you are implementing a new feature, your PR will only be merged if your screenshots are up to date.\
|
|
||||||
Furthermore, feature PRs will only be merged if their summary contains a clear usage description (understandable for users) and testing description (understandable for reviewers).
|
|
||||||
You should strive to combine both into a single description.
|
|
||||||
|
|
||||||
Another requirement for merging PRs is that the PR is labeled correctly.\
|
|
||||||
However, this is not your job as a contributor, but the job of the person merging your PR.\
|
|
||||||
If you think that your PR was labeled incorrectly, or notice that it was merged without labels, please let us know.
|
|
||||||
|
|
||||||
If your PR closes some issues, you must note that in a way that both GitHub and Gitea understand, i.e. by appending a paragraph like
|
If your PR closes some issues, you must note that in a way that both GitHub and Gitea understand, i.e. by appending a paragraph like
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@@ -244,20 +225,17 @@ PRs without a milestone may not be merged.
|
|||||||
|
|
||||||
### Labels
|
### Labels
|
||||||
|
|
||||||
Almost all labels used inside Gitea can be classified as one of the following:
|
Every PR should be labeled correctly with every label that applies. \
|
||||||
|
This includes especially the distinction between `bug` (fixing existing functionality), `feature` (new functionality), `enhancement` (upgrades for existing functionality), and `refactoring` (improving the internal code structure without changing the output (much)). \
|
||||||
- `modifies/…`: Determines which parts of the codebase are affected. These labels will be set through the CI.
|
Furthermore,
|
||||||
- `topic/…`: Determines the conceptual component of Gitea that is affected, i.e. issues, projects, or authentication. At best, PRs should only target one component but there might be overlap. Must be set manually.
|
|
||||||
- `type/…`: Determines the type of an issue or PR (feature, refactoring, docs, bug, …). If GitHub supported scoped labels, these labels would be exclusive, so you should set **exactly** one, not more or less (every PR should fall into one of the provided categories, and only one).
|
|
||||||
- `issue/…` / `pr/…`: Labels that are specific to issues or PRs respectively and that are only necessary in a given context, i.e. `issue/not-a-bug` or `pr/need-2-approvals`
|
|
||||||
|
|
||||||
Every PR should be labeled correctly with every label that applies.
|
|
||||||
|
|
||||||
There are also some labels that will be managed automatically.\
|
|
||||||
In particular, these are
|
|
||||||
|
|
||||||
- the amount of pending required approvals
|
- the amount of pending required approvals
|
||||||
- has all `backport`s or needs a manual backport
|
- whether this PR is `blocked`, a `backport` or `breaking`
|
||||||
|
- if it targets the `ui` or `api`
|
||||||
|
- if it increases the application `speed`
|
||||||
|
- reduces `memory usage`
|
||||||
|
|
||||||
|
are oftentimes notable labels.
|
||||||
|
|
||||||
### Breaking PRs
|
### Breaking PRs
|
||||||
|
|
||||||
@@ -274,16 +252,13 @@ Changing the default value of a setting or replacing the setting with another on
|
|||||||
|
|
||||||
#### How to handle breaking PRs?
|
#### How to handle breaking PRs?
|
||||||
|
|
||||||
If your PR has a breaking change, you must add two things to the summary of your PR:
|
If your PR has a breaking change, you must add a `BREAKING` section to your PR summary, e.g.
|
||||||
|
|
||||||
1. A reasoning why this breaking change is necessary
|
```
|
||||||
2. A `BREAKING` section explaining in simple terms (understandable for a typical user) how this PR affects users and how to mitigate these changes. This section can look for example like
|
|
||||||
|
|
||||||
```md
|
|
||||||
## :warning: BREAKING :warning:
|
## :warning: BREAKING :warning:
|
||||||
```
|
```
|
||||||
|
|
||||||
Breaking PRs will not be merged as long as not both of these requirements are met.
|
To explain how this will affect users and how to mitigate these changes.
|
||||||
|
|
||||||
### Maintaining open PRs
|
### Maintaining open PRs
|
||||||
|
|
||||||
@@ -358,12 +333,11 @@ $REWRITTEN_PR_SUMMARY
|
|||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
If you add a new feature or change an existing aspect of Gitea, the documentation for that feature must be created or updated in another PR at [https://gitea.com/gitea/docs](https://gitea.com/gitea/docs).
|
If you add a new feature or change an existing aspect of Gitea, the documentation for that feature must be created or updated in the same PR.
|
||||||
**The docs directory on main repository will be removed at some time. We will have a yaml file to store configuration file's meta data. After that completed, configuration documentation should be in the main repository.**
|
|
||||||
|
|
||||||
## API v1
|
## API v1
|
||||||
|
|
||||||
The API is documented by [swagger](https://gitea.com/api/swagger) and is based on [the GitHub API](https://docs.github.com/en/rest).
|
The API is documented by [swagger](http://try.gitea.io/api/swagger) and is based on [the GitHub API](https://docs.github.com/en/rest).
|
||||||
|
|
||||||
### GitHub API compatibility
|
### GitHub API compatibility
|
||||||
|
|
||||||
@@ -465,7 +439,7 @@ We assume in good faith that the information you provide is legally binding.
|
|||||||
We adopted a release schedule to streamline the process of working on, finishing, and issuing releases. \
|
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 three or four months, which breaks down into two or three months of general development followed by one month of testing and polishing known as the release freeze. \
|
The overall goal is to make a major release every three or four months, which breaks down into two or three months of general development followed by one month of testing and polishing known as the release freeze. \
|
||||||
All the feature pull requests should be
|
All the feature pull requests should be
|
||||||
merged before feature freeze. All feature pull requests haven't been merged before this feature freeze will be moved to next milestone, please notice our feature freeze announcement on discord. And, during the frozen period, a corresponding
|
merged before feature freeze. And, during the frozen period, a corresponding
|
||||||
release branch is open for fixes backported from main branch. Release candidates
|
release branch is open for fixes backported from main branch. Release candidates
|
||||||
are made during this period for user testing to
|
are made during this period for user testing to
|
||||||
obtain a final version that is maintained in this branch.
|
obtain a final version that is maintained in this branch.
|
||||||
@@ -496,53 +470,36 @@ if possible provide GPG signed commits.
|
|||||||
https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/
|
https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/
|
||||||
https://help.github.com/articles/signing-commits-with-gpg/
|
https://help.github.com/articles/signing-commits-with-gpg/
|
||||||
|
|
||||||
Furthermore, any account with write access (like bots and TOC members) **must** use 2FA.
|
|
||||||
https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/
|
|
||||||
|
|
||||||
## Technical Oversight Committee (TOC)
|
## Technical Oversight Committee (TOC)
|
||||||
|
|
||||||
At the start of 2023, the `Owners` team was dissolved. Instead, the governance charter proposed a technical oversight committee (TOC) which expands the ownership team of the Gitea project from three elected positions to six positions. Three positions are elected as it has been over the past years, and the other three consist of appointed members from the Gitea company.
|
At the start of 2023, the `Owners` team was dissolved. Instead, the governance charter proposed a technical oversight committee (TOC) which expands the ownership team of the Gitea project from three elected positions to six positions. Three positions would be elected as it has been over the past years, and the other three would consist of appointed members from the Gitea company.
|
||||||
https://blog.gitea.com/quarterly-23q1/
|
https://blog.gitea.io/2023/02/gitea-quarterly-report-23q1/
|
||||||
|
|
||||||
### TOC election process
|
When the new community members have been elected, the old members will give up ownership to the newly elected members. For security reasons, TOC members 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/
|
||||||
Any maintainer is eligible to be part of the community TOC if they are not associated with the Gitea company.
|
|
||||||
A maintainer can either nominate themselves, or can be nominated by other maintainers to be a candidate for the TOC election.
|
|
||||||
If you are nominated by someone else, you must first accept your nomination before the vote starts to be a candidate.
|
|
||||||
|
|
||||||
The TOC is elected for one year, the TOC election happens yearly.
|
|
||||||
After the announcement of the results of the TOC election, elected members have two weeks time to confirm or refuse the seat.
|
|
||||||
If an elected member does not answer within this timeframe, they are automatically assumed to refuse the seat.
|
|
||||||
Refusals result in the person with the next highest vote getting the same choice.
|
|
||||||
As long as seats are empty in the TOC, members of the previous TOC can fill them until an elected member accepts the seat.
|
|
||||||
|
|
||||||
If an elected member that accepts the seat does not have 2FA configured yet, they will be temporarily counted as `answer pending` until they manage to configure 2FA, thus leaving their seat empty for this duration.
|
|
||||||
|
|
||||||
### Current TOC members
|
### Current TOC members
|
||||||
|
|
||||||
- 2024-01-01 ~ 2024-12-31
|
- 2023-01-01 ~ 2023-12-31 - https://blog.gitea.io/2023/02/gitea-quarterly-report-23q1/
|
||||||
- Company
|
- Company
|
||||||
- [Jason Song](https://gitea.com/wolfogre) <i@wolfogre.com>
|
- [Jason Song](https://gitea.com/wolfogre) <i@wolfogre.com>
|
||||||
- [Lunny Xiao](https://gitea.com/lunny) <xiaolunwen@gmail.com>
|
- [Lunny Xiao](https://gitea.com/lunny) <xiaolunwen@gmail.com>
|
||||||
- [Matti Ranta](https://gitea.com/techknowlogick) <techknowlogick@gitea.com>
|
- [Matti Ranta](https://gitea.com/techknowlogick) <techknowlogick@gitea.io>
|
||||||
- Community
|
- Community
|
||||||
- [6543](https://gitea.com/6543) <6543@obermui.de>
|
- [6543](https://gitea.com/6543) <6543@obermui.de>
|
||||||
- [delvh](https://gitea.com/delvh) <dev.lh@web.de>
|
- [Andrew Thornton](https://gitea.com/zeripath) <art27@cantab.net>
|
||||||
- [John Olheiser](https://gitea.com/jolheiser) <john.olheiser@gmail.com>
|
- [John Olheiser](https://gitea.com/jolheiser) <john.olheiser@gmail.com>
|
||||||
|
|
||||||
### Previous TOC/owners members
|
### Previous TOC/owners members
|
||||||
|
|
||||||
Here's the history of the owners and the time they served:
|
Here's the history of the owners and the time they served:
|
||||||
|
|
||||||
- [Lunny Xiao](https://gitea.com/lunny) - 2016, 2017, [2018](https://github.com/go-gitea/gitea/issues/3255), [2019](https://github.com/go-gitea/gitea/issues/5572), [2020](https://github.com/go-gitea/gitea/issues/9230), [2021](https://github.com/go-gitea/gitea/issues/13801), [2022](https://github.com/go-gitea/gitea/issues/17872), 2023
|
- [Lunny Xiao](https://gitea.com/lunny) - 2016, 2017, [2018](https://github.com/go-gitea/gitea/issues/3255), [2019](https://github.com/go-gitea/gitea/issues/5572), [2020](https://github.com/go-gitea/gitea/issues/9230), [2021](https://github.com/go-gitea/gitea/issues/13801), [2022](https://github.com/go-gitea/gitea/issues/17872)
|
||||||
- [Kim Carlbäcker](https://github.com/bkcsoft) - 2016, 2017
|
- [Kim Carlbäcker](https://github.com/bkcsoft) - 2016, 2017
|
||||||
- [Thomas Boerger](https://gitea.com/tboerger) - 2016, 2017
|
- [Thomas Boerger](https://gitea.com/tboerger) - 2016, 2017
|
||||||
- [Lauris Bukšis-Haberkorns](https://gitea.com/lafriks) - [2018](https://github.com/go-gitea/gitea/issues/3255), [2019](https://github.com/go-gitea/gitea/issues/5572), [2020](https://github.com/go-gitea/gitea/issues/9230), [2021](https://github.com/go-gitea/gitea/issues/13801)
|
- [Lauris Bukšis-Haberkorns](https://gitea.com/lafriks) - [2018](https://github.com/go-gitea/gitea/issues/3255), [2019](https://github.com/go-gitea/gitea/issues/5572), [2020](https://github.com/go-gitea/gitea/issues/9230), [2021](https://github.com/go-gitea/gitea/issues/13801)
|
||||||
- [Matti Ranta](https://gitea.com/techknowlogick) - [2019](https://github.com/go-gitea/gitea/issues/5572), [2020](https://github.com/go-gitea/gitea/issues/9230), [2021](https://github.com/go-gitea/gitea/issues/13801), [2022](https://github.com/go-gitea/gitea/issues/17872), 2023
|
- [Matti Ranta](https://gitea.com/techknowlogick) - [2019](https://github.com/go-gitea/gitea/issues/5572), [2020](https://github.com/go-gitea/gitea/issues/9230), [2021](https://github.com/go-gitea/gitea/issues/13801), [2022](https://github.com/go-gitea/gitea/issues/17872)
|
||||||
- [Andrew Thornton](https://gitea.com/zeripath) - [2020](https://github.com/go-gitea/gitea/issues/9230), [2021](https://github.com/go-gitea/gitea/issues/13801), [2022](https://github.com/go-gitea/gitea/issues/17872), 2023
|
- [Andrew Thornton](https://gitea.com/zeripath) - [2020](https://github.com/go-gitea/gitea/issues/9230), [2021](https://github.com/go-gitea/gitea/issues/13801), [2022](https://github.com/go-gitea/gitea/issues/17872)
|
||||||
- [6543](https://gitea.com/6543) - 2023
|
|
||||||
- [John Olheiser](https://gitea.com/jolheiser) - 2023
|
|
||||||
- [Jason Song](https://gitea.com/wolfogre) - 2023
|
|
||||||
|
|
||||||
## Governance Compensation
|
## Governance Compensation
|
||||||
|
|
||||||
|
|||||||
50
Dockerfile
50
Dockerfile
@@ -1,47 +1,29 @@
|
|||||||
# Build stage
|
#Build stage
|
||||||
FROM docker.io/library/golang:1.24-alpine3.21 AS build-env
|
FROM docker.io/library/golang:1.20-alpine3.18 AS build-env
|
||||||
|
|
||||||
ARG GOPROXY
|
ARG GOPROXY
|
||||||
ENV GOPROXY=${GOPROXY:-direct}
|
ENV GOPROXY ${GOPROXY:-direct}
|
||||||
|
|
||||||
ARG GITEA_VERSION
|
ARG GITEA_VERSION
|
||||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
ARG TAGS="sqlite sqlite_unlock_notify"
|
||||||
ENV TAGS="bindata timetzdata $TAGS"
|
ENV TAGS "bindata timetzdata $TAGS"
|
||||||
ARG CGO_EXTRA_CFLAGS
|
ARG CGO_EXTRA_CFLAGS
|
||||||
|
|
||||||
# Build deps
|
#Build deps
|
||||||
RUN apk --no-cache add \
|
RUN apk --no-cache add build-base git nodejs npm
|
||||||
build-base \
|
|
||||||
git \
|
|
||||||
nodejs \
|
|
||||||
npm \
|
|
||||||
&& rm -rf /var/cache/apk/*
|
|
||||||
|
|
||||||
# Setup repo
|
#Setup repo
|
||||||
COPY . ${GOPATH}/src/code.gitea.io/gitea
|
COPY . ${GOPATH}/src/code.gitea.io/gitea
|
||||||
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
||||||
|
|
||||||
# Checkout version if set
|
#Checkout version if set
|
||||||
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
||||||
&& make clean-all build
|
&& make clean-all build
|
||||||
|
|
||||||
# Begin env-to-ini build
|
# Begin env-to-ini build
|
||||||
RUN go build contrib/environment-to-ini/environment-to-ini.go
|
RUN go build contrib/environment-to-ini/environment-to-ini.go
|
||||||
|
|
||||||
# Copy local files
|
FROM docker.io/library/alpine:3.18
|
||||||
COPY docker/root /tmp/local
|
|
||||||
|
|
||||||
# Set permissions
|
|
||||||
RUN chmod 755 /tmp/local/usr/bin/entrypoint \
|
|
||||||
/tmp/local/usr/local/bin/gitea \
|
|
||||||
/tmp/local/etc/s6/gitea/* \
|
|
||||||
/tmp/local/etc/s6/openssh/* \
|
|
||||||
/tmp/local/etc/s6/.s6-svscan/* \
|
|
||||||
/go/src/code.gitea.io/gitea/gitea \
|
|
||||||
/go/src/code.gitea.io/gitea/environment-to-ini
|
|
||||||
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
|
|
||||||
|
|
||||||
FROM docker.io/library/alpine:3.21
|
|
||||||
LABEL maintainer="maintainers@gitea.io"
|
LABEL maintainer="maintainers@gitea.io"
|
||||||
|
|
||||||
EXPOSE 22 3000
|
EXPOSE 22 3000
|
||||||
@@ -57,8 +39,7 @@ RUN apk --no-cache add \
|
|||||||
s6 \
|
s6 \
|
||||||
sqlite \
|
sqlite \
|
||||||
su-exec \
|
su-exec \
|
||||||
gnupg \
|
gnupg
|
||||||
&& rm -rf /var/cache/apk/*
|
|
||||||
|
|
||||||
RUN addgroup \
|
RUN addgroup \
|
||||||
-S -g 1000 \
|
-S -g 1000 \
|
||||||
@@ -72,15 +53,18 @@ RUN addgroup \
|
|||||||
git && \
|
git && \
|
||||||
echo "git:*" | chpasswd -e
|
echo "git:*" | chpasswd -e
|
||||||
|
|
||||||
ENV USER=git
|
ENV USER git
|
||||||
ENV GITEA_CUSTOM=/data/gitea
|
ENV GITEA_CUSTOM /data/gitea
|
||||||
|
|
||||||
VOLUME ["/data"]
|
VOLUME ["/data"]
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/bin/entrypoint"]
|
ENTRYPOINT ["/usr/bin/entrypoint"]
|
||||||
CMD ["/usr/bin/s6-svscan", "/etc/s6"]
|
CMD ["/bin/s6-svscan", "/etc/s6"]
|
||||||
|
|
||||||
COPY --from=build-env /tmp/local /
|
COPY docker/root /
|
||||||
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
||||||
COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
||||||
COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh
|
COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh
|
||||||
|
RUN chmod 755 /usr/bin/entrypoint /app/gitea/gitea /usr/local/bin/gitea /usr/local/bin/environment-to-ini
|
||||||
|
RUN chmod 755 /etc/s6/gitea/* /etc/s6/openssh/* /etc/s6/.s6-svscan/*
|
||||||
|
RUN chmod 644 /etc/profile.d/gitea_bash_autocomplete.sh
|
||||||
|
|||||||
@@ -1,45 +1,29 @@
|
|||||||
# Build stage
|
#Build stage
|
||||||
FROM docker.io/library/golang:1.24-alpine3.21 AS build-env
|
FROM docker.io/library/golang:1.20-alpine3.18 AS build-env
|
||||||
|
|
||||||
ARG GOPROXY
|
ARG GOPROXY
|
||||||
ENV GOPROXY=${GOPROXY:-direct}
|
ENV GOPROXY ${GOPROXY:-direct}
|
||||||
|
|
||||||
ARG GITEA_VERSION
|
ARG GITEA_VERSION
|
||||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
ARG TAGS="sqlite sqlite_unlock_notify"
|
||||||
ENV TAGS="bindata timetzdata $TAGS"
|
ENV TAGS "bindata timetzdata $TAGS"
|
||||||
ARG CGO_EXTRA_CFLAGS
|
ARG CGO_EXTRA_CFLAGS
|
||||||
|
|
||||||
#Build deps
|
#Build deps
|
||||||
RUN apk --no-cache add \
|
RUN apk --no-cache add build-base git nodejs npm
|
||||||
build-base \
|
|
||||||
git \
|
|
||||||
nodejs \
|
|
||||||
npm \
|
|
||||||
&& rm -rf /var/cache/apk/*
|
|
||||||
|
|
||||||
# Setup repo
|
#Setup repo
|
||||||
COPY . ${GOPATH}/src/code.gitea.io/gitea
|
COPY . ${GOPATH}/src/code.gitea.io/gitea
|
||||||
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
||||||
|
|
||||||
# Checkout version if set
|
#Checkout version if set
|
||||||
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
||||||
&& make clean-all build
|
&& make clean-all build
|
||||||
|
|
||||||
# Begin env-to-ini build
|
# Begin env-to-ini build
|
||||||
RUN go build contrib/environment-to-ini/environment-to-ini.go
|
RUN go build contrib/environment-to-ini/environment-to-ini.go
|
||||||
|
|
||||||
# Copy local files
|
FROM docker.io/library/alpine:3.18
|
||||||
COPY docker/rootless /tmp/local
|
|
||||||
|
|
||||||
# Set permissions
|
|
||||||
RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
|
|
||||||
/tmp/local/usr/local/bin/docker-setup.sh \
|
|
||||||
/tmp/local/usr/local/bin/gitea \
|
|
||||||
/go/src/code.gitea.io/gitea/gitea \
|
|
||||||
/go/src/code.gitea.io/gitea/environment-to-ini
|
|
||||||
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
|
|
||||||
|
|
||||||
FROM docker.io/library/alpine:3.21
|
|
||||||
LABEL maintainer="maintainers@gitea.io"
|
LABEL maintainer="maintainers@gitea.io"
|
||||||
|
|
||||||
EXPOSE 2222 3000
|
EXPOSE 2222 3000
|
||||||
@@ -51,8 +35,7 @@ RUN apk --no-cache add \
|
|||||||
gettext \
|
gettext \
|
||||||
git \
|
git \
|
||||||
curl \
|
curl \
|
||||||
gnupg \
|
gnupg
|
||||||
&& rm -rf /var/cache/apk/*
|
|
||||||
|
|
||||||
RUN addgroup \
|
RUN addgroup \
|
||||||
-S -g 1000 \
|
-S -g 1000 \
|
||||||
@@ -68,23 +51,26 @@ RUN addgroup \
|
|||||||
RUN mkdir -p /var/lib/gitea /etc/gitea
|
RUN mkdir -p /var/lib/gitea /etc/gitea
|
||||||
RUN chown git:git /var/lib/gitea /etc/gitea
|
RUN chown git:git /var/lib/gitea /etc/gitea
|
||||||
|
|
||||||
COPY --from=build-env /tmp/local /
|
COPY docker/rootless /
|
||||||
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
||||||
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
||||||
COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh
|
COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh
|
||||||
|
RUN chmod 755 /usr/local/bin/docker-entrypoint.sh /usr/local/bin/docker-setup.sh /app/gitea/gitea /usr/local/bin/gitea /usr/local/bin/environment-to-ini
|
||||||
|
RUN chmod 644 /etc/profile.d/gitea_bash_autocomplete.sh
|
||||||
|
|
||||||
# git:git
|
#git:git
|
||||||
USER 1000:1000
|
USER 1000:1000
|
||||||
ENV GITEA_WORK_DIR=/var/lib/gitea
|
ENV GITEA_WORK_DIR /var/lib/gitea
|
||||||
ENV GITEA_CUSTOM=/var/lib/gitea/custom
|
ENV GITEA_CUSTOM /var/lib/gitea/custom
|
||||||
ENV GITEA_TEMP=/tmp/gitea
|
ENV GITEA_TEMP /tmp/gitea
|
||||||
ENV TMPDIR=/tmp/gitea
|
ENV TMPDIR /tmp/gitea
|
||||||
|
|
||||||
# TODO add to docs the ability to define the ini to load (useful to test and revert a config)
|
#TODO add to docs the ability to define the ini to load (useful to test and revert a config)
|
||||||
ENV GITEA_APP_INI=/etc/gitea/app.ini
|
ENV GITEA_APP_INI /etc/gitea/app.ini
|
||||||
ENV HOME="/var/lib/gitea/git"
|
ENV HOME "/var/lib/gitea/git"
|
||||||
VOLUME ["/var/lib/gitea", "/etc/gitea"]
|
VOLUME ["/var/lib/gitea", "/etc/gitea"]
|
||||||
WORKDIR /var/lib/gitea
|
WORKDIR /var/lib/gitea
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/bin/dumb-init", "--", "/usr/local/bin/docker-entrypoint.sh"]
|
ENTRYPOINT ["/usr/bin/dumb-init", "--", "/usr/local/bin/docker-entrypoint.sh"]
|
||||||
CMD []
|
CMD []
|
||||||
|
|
||||||
|
|||||||
16
MAINTAINERS
16
MAINTAINERS
@@ -31,6 +31,7 @@ Gary Kim <gary@garykim.dev> (@gary-kim)
|
|||||||
Guillermo Prandi <gitea.maint@mailfilter.com.ar> (@guillep2k)
|
Guillermo Prandi <gitea.maint@mailfilter.com.ar> (@guillep2k)
|
||||||
Mura Li <typeless@ctli.io> (@typeless)
|
Mura Li <typeless@ctli.io> (@typeless)
|
||||||
6543 <6543@obermui.de> (@6543)
|
6543 <6543@obermui.de> (@6543)
|
||||||
|
jaqra <jaqra@hotmail.com> (@jaqra)
|
||||||
David Svantesson <davidsvantesson@gmail.com> (@davidsvantesson)
|
David Svantesson <davidsvantesson@gmail.com> (@davidsvantesson)
|
||||||
a1012112796 <1012112796@qq.com> (@a1012112796)
|
a1012112796 <1012112796@qq.com> (@a1012112796)
|
||||||
Karl Heinz Marbaise <kama@soebes.de> (@khmarbaise)
|
Karl Heinz Marbaise <kama@soebes.de> (@khmarbaise)
|
||||||
@@ -45,22 +46,9 @@ Wim <wim@42.be> (@42wim)
|
|||||||
Jason Song <i@wolfogre.com> (@wolfogre)
|
Jason Song <i@wolfogre.com> (@wolfogre)
|
||||||
Yarden Shoham <git@yardenshoham.com> (@yardenshoham)
|
Yarden Shoham <git@yardenshoham.com> (@yardenshoham)
|
||||||
Yu Tian <zettat123@gmail.com> (@Zettat123)
|
Yu Tian <zettat123@gmail.com> (@Zettat123)
|
||||||
|
Eddie Yang <576951401@qq.com> (@yp05327)
|
||||||
Dong Ge <gedong_1994@163.com> (@sillyguodong)
|
Dong Ge <gedong_1994@163.com> (@sillyguodong)
|
||||||
Xinyi Gong <hestergong@gmail.com> (@HesterG)
|
Xinyi Gong <hestergong@gmail.com> (@HesterG)
|
||||||
wxiaoguang <wxiaoguang@gmail.com> (@wxiaoguang)
|
wxiaoguang <wxiaoguang@gmail.com> (@wxiaoguang)
|
||||||
Gary Moon <gary@garymoon.net> (@garymoon)
|
Gary Moon <gary@garymoon.net> (@garymoon)
|
||||||
Philip Peterson <philip.c.peterson@gmail.com> (@philip-peterson)
|
Philip Peterson <philip.c.peterson@gmail.com> (@philip-peterson)
|
||||||
Denys Konovalov <kontakt@denyskon.de> (@denyskon)
|
|
||||||
Punit Inani <punitinani1@gmail.com> (@puni9869)
|
|
||||||
CaiCandong <1290147055@qq.com> (@caicandong)
|
|
||||||
Rui Chen <rui@chenrui.dev> (@chenrui333)
|
|
||||||
Nanguan Lin <nanguanlin6@gmail.com> (@lng2020)
|
|
||||||
kerwin612 <kerwin612@qq.com> (@kerwin612)
|
|
||||||
Gary Wang <git@blumia.net> (@BLumia)
|
|
||||||
Tim-Niclas Oelschläger <zokki.softwareschmiede@gmail.com> (@zokkis)
|
|
||||||
Yu Liu <1240335630@qq.com> (@HEREYUA)
|
|
||||||
Kemal Zebari <kemalzebra@gmail.com> (@kemzeb)
|
|
||||||
Rowan Bohde <rowan.bohde@gmail.com> (@bohde)
|
|
||||||
hiifong <i@hiif.ong> (@hiifong)
|
|
||||||
metiftikci <metiftikci@hotmail.com> (@metiftikci)
|
|
||||||
Christopher Homberger <christopher.homberger@web.de> (@ChristopherHX)
|
|
||||||
|
|||||||
504
Makefile
504
Makefile
@@ -23,38 +23,36 @@ SHASUM ?= shasum -a 256
|
|||||||
HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes)
|
HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes)
|
||||||
COMMA := ,
|
COMMA := ,
|
||||||
|
|
||||||
XGO_VERSION := go-1.24.x
|
XGO_VERSION := go-1.20.x
|
||||||
|
|
||||||
AIR_PACKAGE ?= github.com/air-verse/air@v1
|
AIR_PACKAGE ?= github.com/cosmtrek/air@v1.43.0
|
||||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.2.1
|
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0
|
||||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0
|
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.5.0
|
||||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.0.2
|
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.52.2
|
||||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.12
|
GXZ_PAGAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
|
||||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.6.0
|
MISSPELL_PACKAGE ?= github.com/client9/misspell/cmd/misspell@v0.3.4
|
||||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0
|
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.4
|
||||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||||
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
|
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0
|
||||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1
|
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@latest
|
||||||
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1
|
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@latest
|
||||||
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.17.1
|
|
||||||
|
|
||||||
DOCKER_IMAGE ?= gitea/gitea
|
DOCKER_IMAGE ?= gitea/gitea
|
||||||
DOCKER_TAG ?= latest
|
DOCKER_TAG ?= latest
|
||||||
DOCKER_REF := $(DOCKER_IMAGE):$(DOCKER_TAG)
|
DOCKER_REF := $(DOCKER_IMAGE):$(DOCKER_TAG)
|
||||||
|
|
||||||
ifeq ($(HAS_GO), yes)
|
ifeq ($(HAS_GO), yes)
|
||||||
|
GOPATH ?= $(shell $(GO) env GOPATH)
|
||||||
|
export PATH := $(GOPATH)/bin:$(PATH)
|
||||||
|
|
||||||
CGO_EXTRA_CFLAGS := -DSQLITE_MAX_VARIABLE_NUMBER=32766
|
CGO_EXTRA_CFLAGS := -DSQLITE_MAX_VARIABLE_NUMBER=32766
|
||||||
CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS)
|
CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(GOOS),windows)
|
ifeq ($(OS), Windows_NT)
|
||||||
IS_WINDOWS := yes
|
GOFLAGS := -v -buildmode=exe
|
||||||
else ifeq ($(patsubst Windows%,Windows,$(OS)),Windows)
|
EXECUTABLE ?= gitea.exe
|
||||||
ifeq ($(GOOS),)
|
else ifeq ($(OS), Windows)
|
||||||
IS_WINDOWS := yes
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
ifeq ($(IS_WINDOWS),yes)
|
|
||||||
GOFLAGS := -v -buildmode=exe
|
GOFLAGS := -v -buildmode=exe
|
||||||
EXECUTABLE ?= gitea.exe
|
EXECUTABLE ?= gitea.exe
|
||||||
else
|
else
|
||||||
@@ -70,10 +68,9 @@ endif
|
|||||||
|
|
||||||
EXTRA_GOFLAGS ?=
|
EXTRA_GOFLAGS ?=
|
||||||
|
|
||||||
MAKE_VERSION := $(shell "$(MAKE)" -v | cat | head -n 1)
|
MAKE_VERSION := $(shell "$(MAKE)" -v | head -n 1)
|
||||||
MAKE_EVIDENCE_DIR := .make_evidence
|
MAKE_EVIDENCE_DIR := .make_evidence
|
||||||
|
|
||||||
GOTESTFLAGS ?=
|
|
||||||
ifeq ($(RACE_ENABLED),true)
|
ifeq ($(RACE_ENABLED),true)
|
||||||
GOFLAGS += -race
|
GOFLAGS += -race
|
||||||
GOTESTFLAGS += -race
|
GOTESTFLAGS += -race
|
||||||
@@ -85,12 +82,18 @@ HUGO_VERSION ?= 0.111.3
|
|||||||
GITHUB_REF_TYPE ?= branch
|
GITHUB_REF_TYPE ?= branch
|
||||||
GITHUB_REF_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
|
GITHUB_REF_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
|
||||||
|
|
||||||
|
# backwards compatible to build with Drone
|
||||||
|
ifneq ($(DRONE_TAG),)
|
||||||
|
GITHUB_REF_TYPE := tag
|
||||||
|
GITHUB_REF_NAME := $(DRONE_TAG)
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq ($(GITHUB_REF_TYPE),branch)
|
ifneq ($(GITHUB_REF_TYPE),branch)
|
||||||
VERSION ?= $(subst v,,$(GITHUB_REF_NAME))
|
VERSION ?= $(subst v,,$(GITHUB_REF_NAME))
|
||||||
GITEA_VERSION ?= $(VERSION)
|
GITEA_VERSION ?= $(VERSION)
|
||||||
else
|
else
|
||||||
ifneq ($(GITHUB_REF_NAME),)
|
ifneq ($(GITHUB_REF_NAME),)
|
||||||
VERSION ?= $(subst release/v,,$(GITHUB_REF_NAME))-nightly
|
VERSION ?= $(subst release/v,,$(GITHUB_REF_NAME))
|
||||||
else
|
else
|
||||||
VERSION ?= main
|
VERSION ?= main
|
||||||
endif
|
endif
|
||||||
@@ -110,22 +113,24 @@ endif
|
|||||||
|
|
||||||
LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
|
LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
|
||||||
|
|
||||||
LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/riscv64
|
LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64
|
||||||
|
|
||||||
|
GO_PACKAGES ?= $(filter-out code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/))
|
||||||
GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/))
|
GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/))
|
||||||
MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list code.gitea.io/gitea/models/migrations/...)
|
|
||||||
|
FOMANTIC_WORK_DIR := web_src/fomantic
|
||||||
|
|
||||||
WEBPACK_SOURCES := $(shell find web_src/js web_src/css -type f)
|
WEBPACK_SOURCES := $(shell find web_src/js web_src/css -type f)
|
||||||
WEBPACK_CONFIGS := webpack.config.js tailwind.config.js
|
WEBPACK_CONFIGS := webpack.config.js
|
||||||
WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css
|
WEBPACK_DEST := public/js/index.js public/css/index.css
|
||||||
WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts
|
WEBPACK_DEST_ENTRIES := public/js public/css public/fonts public/img/webpack
|
||||||
|
|
||||||
BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
|
BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
|
||||||
BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))
|
BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))
|
||||||
|
|
||||||
GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go
|
GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go
|
||||||
|
|
||||||
SVG_DEST_DIR := public/assets/img/svg
|
SVG_DEST_DIR := public/img/svg
|
||||||
|
|
||||||
AIR_TMP_DIR := .air
|
AIR_TMP_DIR := .air
|
||||||
|
|
||||||
@@ -136,18 +141,13 @@ TAGS ?=
|
|||||||
TAGS_SPLIT := $(subst $(COMMA), ,$(TAGS))
|
TAGS_SPLIT := $(subst $(COMMA), ,$(TAGS))
|
||||||
TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
|
TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
|
||||||
|
|
||||||
TEST_TAGS ?= $(TAGS_SPLIT) sqlite sqlite_unlock_notify
|
TEST_TAGS ?= sqlite sqlite_unlock_notify
|
||||||
|
|
||||||
TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR) $(GO_LICENSE_TMP_DIR)
|
TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMANTIC_WORK_DIR)/node_modules $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR) $(GO_LICENSE_TMP_DIR)
|
||||||
|
|
||||||
GO_DIRS := build cmd models modules routers services tests
|
GO_DIRS := build cmd models modules routers services tests
|
||||||
WEB_DIRS := web_src/js web_src/css
|
WEB_DIRS := web_src/js web_src/css
|
||||||
|
|
||||||
ESLINT_FILES := web_src/js tools *.js *.ts *.cjs tests/e2e
|
|
||||||
STYLELINT_FILES := web_src/css web_src/js/components/*.vue
|
|
||||||
SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.js *.md *.yml *.yaml *.toml)) $(filter-out tools/misspellings.csv, $(wildcard tools/*))
|
|
||||||
EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini
|
|
||||||
|
|
||||||
GO_SOURCES := $(wildcard *.go)
|
GO_SOURCES := $(wildcard *.go)
|
||||||
GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" ! -path modules/options/bindata.go ! -path modules/public/bindata.go ! -path modules/templates/bindata.go)
|
GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" ! -path modules/options/bindata.go ! -path modules/public/bindata.go ! -path modules/templates/bindata.go)
|
||||||
GO_SOURCES += $(GENERATED_GO_DEST)
|
GO_SOURCES += $(GENERATED_GO_DEST)
|
||||||
@@ -164,19 +164,24 @@ ifdef DEPS_PLAYWRIGHT
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
|
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
|
||||||
SWAGGER_SPEC_INPUT := templates/swagger/v1_input.json
|
SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|g
|
||||||
|
SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|"basePath": "/api/v1"|g
|
||||||
SWAGGER_EXCLUDE := code.gitea.io/sdk
|
SWAGGER_EXCLUDE := code.gitea.io/sdk
|
||||||
|
SWAGGER_NEWLINE_COMMAND := -e '$$a\'
|
||||||
|
|
||||||
TEST_MYSQL_HOST ?= mysql:3306
|
TEST_MYSQL_HOST ?= mysql:3306
|
||||||
TEST_MYSQL_DBNAME ?= testgitea
|
TEST_MYSQL_DBNAME ?= testgitea
|
||||||
TEST_MYSQL_USERNAME ?= root
|
TEST_MYSQL_USERNAME ?= root
|
||||||
TEST_MYSQL_PASSWORD ?=
|
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_HOST ?= pgsql:5432
|
||||||
TEST_PGSQL_DBNAME ?= testgitea
|
TEST_PGSQL_DBNAME ?= testgitea
|
||||||
TEST_PGSQL_USERNAME ?= postgres
|
TEST_PGSQL_USERNAME ?= postgres
|
||||||
TEST_PGSQL_PASSWORD ?= postgres
|
TEST_PGSQL_PASSWORD ?= postgres
|
||||||
TEST_PGSQL_SCHEMA ?= gtestschema
|
TEST_PGSQL_SCHEMA ?= gtestschema
|
||||||
TEST_MINIO_ENDPOINT ?= minio:9000
|
|
||||||
TEST_MSSQL_HOST ?= mssql:1433
|
TEST_MSSQL_HOST ?= mssql:1433
|
||||||
TEST_MSSQL_DBNAME ?= gitea
|
TEST_MSSQL_DBNAME ?= gitea
|
||||||
TEST_MSSQL_USERNAME ?= sa
|
TEST_MSSQL_USERNAME ?= sa
|
||||||
@@ -186,11 +191,58 @@ TEST_MSSQL_PASSWORD ?= MwantsaSecurePassword1
|
|||||||
all: build
|
all: build
|
||||||
|
|
||||||
.PHONY: help
|
.PHONY: help
|
||||||
help: Makefile ## print Makefile help information.
|
help:
|
||||||
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m[TARGETS] default target: build\033[0m\n\n\033[35mTargets:\033[0m\n"} /^[0-9A-Za-z._-]+:.*?##/ { printf " \033[36m%-45s\033[0m %s\n", $$1, $$2 }' Makefile #$(MAKEFILE_LIST)
|
@echo "Make Routines:"
|
||||||
@printf " \033[36m%-46s\033[0m %s\n" "test-e2e[#TestSpecificName]" "test end to end using playwright"
|
@echo " - \"\" equivalent to \"build\""
|
||||||
@printf " \033[36m%-46s\033[0m %s\n" "test[#TestSpecificName]" "run unit test"
|
@echo " - build build everything"
|
||||||
@printf " \033[36m%-46s\033[0m %s\n" "test-sqlite[#TestSpecificName]" "run integration test for sqlite"
|
@echo " - frontend build frontend files"
|
||||||
|
@echo " - backend build backend files"
|
||||||
|
@echo " - watch watch everything and continuously rebuild"
|
||||||
|
@echo " - watch-frontend watch frontend files and continuously rebuild"
|
||||||
|
@echo " - watch-backend watch backend files and continuously rebuild"
|
||||||
|
@echo " - clean delete backend and integration files"
|
||||||
|
@echo " - clean-all delete backend, frontend and integration files"
|
||||||
|
@echo " - deps install dependencies"
|
||||||
|
@echo " - deps-frontend install frontend dependencies"
|
||||||
|
@echo " - deps-backend install backend dependencies"
|
||||||
|
@echo " - deps-tools install tool dependencies"
|
||||||
|
@echo " - lint lint everything"
|
||||||
|
@echo " - lint-fix lint everything and fix issues"
|
||||||
|
@echo " - lint-actions lint action workflow files"
|
||||||
|
@echo " - lint-frontend lint frontend files"
|
||||||
|
@echo " - lint-frontend-fix lint frontend files and fix issues"
|
||||||
|
@echo " - lint-backend lint backend files"
|
||||||
|
@echo " - lint-backend-fix lint backend files and fix issues"
|
||||||
|
@echo " - lint-go lint go files"
|
||||||
|
@echo " - lint-go-fix lint go files and fix issues"
|
||||||
|
@echo " - lint-go-vet lint go files with vet"
|
||||||
|
@echo " - lint-js lint js files"
|
||||||
|
@echo " - lint-js-fix lint js files and fix issues"
|
||||||
|
@echo " - lint-css lint css files"
|
||||||
|
@echo " - lint-css-fix lint css files and fix issues"
|
||||||
|
@echo " - lint-md lint markdown files"
|
||||||
|
@echo " - lint-swagger lint swagger files"
|
||||||
|
@echo " - checks run various consistency checks"
|
||||||
|
@echo " - checks-frontend check frontend files"
|
||||||
|
@echo " - checks-backend check backend files"
|
||||||
|
@echo " - test test everything"
|
||||||
|
@echo " - test-frontend test frontend files"
|
||||||
|
@echo " - test-backend test backend files"
|
||||||
|
@echo " - test-e2e[\#TestSpecificName] test end to end using playwright"
|
||||||
|
@echo " - webpack build webpack files"
|
||||||
|
@echo " - svg build svg files"
|
||||||
|
@echo " - fomantic build fomantic files"
|
||||||
|
@echo " - generate run \"go generate\""
|
||||||
|
@echo " - fmt format the Go code"
|
||||||
|
@echo " - generate-license update license files"
|
||||||
|
@echo " - generate-gitignore update gitignore files"
|
||||||
|
@echo " - generate-manpage generate manpage"
|
||||||
|
@echo " - generate-swagger generate the swagger spec from code comments"
|
||||||
|
@echo " - swagger-validate check if the swagger spec is valid"
|
||||||
|
@echo " - go-licenses regenerate go licenses"
|
||||||
|
@echo " - tidy run go mod tidy"
|
||||||
|
@echo " - test[\#TestSpecificName] run unit test"
|
||||||
|
@echo " - test-sqlite[\#TestSpecificName] run integration test for sqlite"
|
||||||
|
|
||||||
.PHONY: go-check
|
.PHONY: go-check
|
||||||
go-check:
|
go-check:
|
||||||
@@ -221,24 +273,25 @@ node-check:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
.PHONY: clean-all
|
.PHONY: clean-all
|
||||||
clean-all: clean ## delete backend, frontend and integration files
|
clean-all: clean
|
||||||
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules
|
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean: ## delete backend and integration files
|
clean:
|
||||||
|
$(GO) clean -i ./...
|
||||||
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) $(BINDATA_HASH) \
|
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) $(BINDATA_HASH) \
|
||||||
integrations*.test \
|
integrations*.test \
|
||||||
e2e*.test \
|
e2e*.test \
|
||||||
tests/integration/gitea-integration-* \
|
tests/integration/gitea-integration-pgsql/ tests/integration/gitea-integration-mysql/ tests/integration/gitea-integration-mysql8/ tests/integration/gitea-integration-sqlite/ \
|
||||||
tests/integration/indexers-* \
|
tests/integration/gitea-integration-mssql/ tests/integration/indexers-mysql/ tests/integration/indexers-mysql8/ tests/integration/indexers-pgsql tests/integration/indexers-sqlite \
|
||||||
tests/mysql.ini tests/pgsql.ini tests/mssql.ini man/ \
|
tests/integration/indexers-mssql tests/mysql.ini tests/mysql8.ini tests/pgsql.ini tests/mssql.ini man/ \
|
||||||
tests/e2e/gitea-e2e-*/ \
|
tests/e2e/gitea-e2e-pgsql/ tests/e2e/gitea-e2e-mysql/ tests/e2e/gitea-e2e-mysql8/ tests/e2e/gitea-e2e-sqlite/ \
|
||||||
tests/e2e/indexers-*/ \
|
tests/e2e/gitea-e2e-mssql/ tests/e2e/indexers-mysql/ tests/e2e/indexers-mysql8/ tests/e2e/indexers-pgsql/ tests/e2e/indexers-sqlite/ \
|
||||||
tests/e2e/reports/ tests/e2e/test-artifacts/ tests/e2e/test-snapshots/
|
tests/e2e/indexers-mssql/ tests/e2e/reports/ tests/e2e/test-artifacts/ tests/e2e/test-snapshots/
|
||||||
|
|
||||||
.PHONY: fmt
|
.PHONY: fmt
|
||||||
fmt: ## format the Go code
|
fmt:
|
||||||
@GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
|
GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
|
||||||
$(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl'))
|
$(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl'))
|
||||||
@# strip whitespace after '{{' or '(' and before '}}' or ')' unless there is only
|
@# strip whitespace after '{{' or '(' and before '}}' or ')' unless there is only
|
||||||
@# whitespace before it
|
@# whitespace before it
|
||||||
@@ -252,10 +305,14 @@ fmt-check: fmt
|
|||||||
@diff=$$(git diff --color=always $(GO_SOURCES) templates $(WEB_DIRS)); \
|
@diff=$$(git diff --color=always $(GO_SOURCES) templates $(WEB_DIRS)); \
|
||||||
if [ -n "$$diff" ]; then \
|
if [ -n "$$diff" ]; then \
|
||||||
echo "Please run 'make fmt' and commit the result:"; \
|
echo "Please run 'make fmt' and commit the result:"; \
|
||||||
printf "%s" "$${diff}"; \
|
echo "$${diff}"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
.PHONY: misspell-check
|
||||||
|
misspell-check:
|
||||||
|
go run $(MISSPELL_PACKAGE) -error $(GO_DIRS) $(WEB_DIRS)
|
||||||
|
|
||||||
.PHONY: $(TAGS_EVIDENCE)
|
.PHONY: $(TAGS_EVIDENCE)
|
||||||
$(TAGS_EVIDENCE):
|
$(TAGS_EVIDENCE):
|
||||||
@mkdir -p $(MAKE_EVIDENCE_DIR)
|
@mkdir -p $(MAKE_EVIDENCE_DIR)
|
||||||
@@ -266,95 +323,85 @@ TAGS_PREREQ := $(TAGS_EVIDENCE)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: generate-swagger
|
.PHONY: generate-swagger
|
||||||
generate-swagger: $(SWAGGER_SPEC) ## generate the swagger spec from code comments
|
generate-swagger: $(SWAGGER_SPEC)
|
||||||
|
|
||||||
$(SWAGGER_SPEC): $(GO_SOURCES_NO_BINDATA) $(SWAGGER_SPEC_INPUT)
|
$(SWAGGER_SPEC): $(GO_SOURCES_NO_BINDATA)
|
||||||
$(GO) run $(SWAGGER_PACKAGE) generate spec --exclude "$(SWAGGER_EXCLUDE)" --input "$(SWAGGER_SPEC_INPUT)" --output './$(SWAGGER_SPEC)'
|
$(GO) run $(SWAGGER_PACKAGE) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
|
||||||
|
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||||
|
$(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
|
||||||
|
|
||||||
.PHONY: swagger-check
|
.PHONY: swagger-check
|
||||||
swagger-check: generate-swagger
|
swagger-check: generate-swagger
|
||||||
@diff=$$(git diff --color=always '$(SWAGGER_SPEC)'); \
|
@diff=$$(git diff --color=always '$(SWAGGER_SPEC)'); \
|
||||||
if [ -n "$$diff" ]; then \
|
if [ -n "$$diff" ]; then \
|
||||||
echo "Please run 'make generate-swagger' and commit the result:"; \
|
echo "Please run 'make generate-swagger' and commit the result:"; \
|
||||||
printf "%s" "$${diff}"; \
|
echo "$${diff}"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
.PHONY: swagger-validate
|
.PHONY: swagger-validate
|
||||||
swagger-validate: ## check if the swagger spec is valid
|
swagger-validate:
|
||||||
@# swagger "validate" requires that the "basePath" must start with a slash, but we are using Golang template "{{...}}"
|
$(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)'
|
||||||
@$(SED_INPLACE) -E -e 's|"basePath":( *)"(.*)"|"basePath":\1"/\2"|g' './$(SWAGGER_SPEC)' # add a prefix slash to basePath
|
|
||||||
@# FIXME: there are some warnings
|
|
||||||
$(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)'
|
$(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)'
|
||||||
@$(SED_INPLACE) -E -e 's|"basePath":( *)"/(.*)"|"basePath":\1"\2"|g' './$(SWAGGER_SPEC)' # remove the prefix slash from basePath
|
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||||
|
|
||||||
.PHONY: checks
|
.PHONY: checks
|
||||||
checks: checks-frontend checks-backend ## run various consistency checks
|
checks: checks-frontend checks-backend
|
||||||
|
|
||||||
.PHONY: checks-frontend
|
.PHONY: checks-frontend
|
||||||
checks-frontend: lockfile-check svg-check ## check frontend files
|
checks-frontend: lockfile-check svg-check
|
||||||
|
|
||||||
.PHONY: checks-backend
|
.PHONY: checks-backend
|
||||||
checks-backend: tidy-check swagger-check fmt-check swagger-validate security-check ## check backend files
|
checks-backend: tidy-check swagger-check fmt-check misspell-check swagger-validate security-check
|
||||||
|
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint: lint-frontend lint-backend lint-spell ## lint everything
|
lint: lint-frontend lint-backend
|
||||||
|
|
||||||
.PHONY: lint-fix
|
.PHONY: lint-fix
|
||||||
lint-fix: lint-frontend-fix lint-backend-fix lint-spell-fix ## lint everything and fix issues
|
lint-fix: lint-frontend-fix lint-backend-fix
|
||||||
|
|
||||||
.PHONY: lint-frontend
|
.PHONY: lint-frontend
|
||||||
lint-frontend: lint-js lint-css ## lint frontend files
|
lint-frontend: lint-js lint-css lint-md lint-swagger
|
||||||
|
|
||||||
.PHONY: lint-frontend-fix
|
.PHONY: lint-frontend-fix
|
||||||
lint-frontend-fix: lint-js-fix lint-css-fix ## lint frontend files and fix issues
|
lint-frontend-fix: lint-js-fix lint-css-fix lint-md lint-swagger
|
||||||
|
|
||||||
.PHONY: lint-backend
|
.PHONY: lint-backend
|
||||||
lint-backend: lint-go lint-go-gitea-vet lint-go-gopls lint-editorconfig ## lint backend files
|
lint-backend: lint-go lint-go-vet lint-editorconfig
|
||||||
|
|
||||||
.PHONY: lint-backend-fix
|
.PHONY: lint-backend-fix
|
||||||
lint-backend-fix: lint-go-fix lint-go-gitea-vet lint-editorconfig ## lint backend files and fix issues
|
lint-backend-fix: lint-go-fix lint-go-vet lint-editorconfig
|
||||||
|
|
||||||
.PHONY: lint-js
|
.PHONY: lint-js
|
||||||
lint-js: node_modules ## lint js files
|
lint-js: node_modules
|
||||||
npx eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES)
|
npx eslint --color --max-warnings=0 --ext js,vue web_src/js build *.config.js tests/e2e
|
||||||
npx vue-tsc
|
|
||||||
|
|
||||||
.PHONY: lint-js-fix
|
.PHONY: lint-js-fix
|
||||||
lint-js-fix: node_modules ## lint js files and fix issues
|
lint-js-fix: node_modules
|
||||||
npx eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES) --fix
|
npx eslint --color --max-warnings=0 --ext js,vue web_src/js build *.config.js tests/e2e --fix
|
||||||
npx vue-tsc
|
|
||||||
|
|
||||||
.PHONY: lint-css
|
.PHONY: lint-css
|
||||||
lint-css: node_modules ## lint css files
|
lint-css: node_modules
|
||||||
npx stylelint --color --max-warnings=0 $(STYLELINT_FILES)
|
npx stylelint --color --max-warnings=0 web_src/css web_src/js/components/*.vue
|
||||||
|
|
||||||
.PHONY: lint-css-fix
|
.PHONY: lint-css-fix
|
||||||
lint-css-fix: node_modules ## lint css files and fix issues
|
lint-css-fix: node_modules
|
||||||
npx stylelint --color --max-warnings=0 $(STYLELINT_FILES) --fix
|
npx stylelint --color --max-warnings=0 web_src/css web_src/js/components/*.vue --fix
|
||||||
|
|
||||||
.PHONY: lint-swagger
|
.PHONY: lint-swagger
|
||||||
lint-swagger: node_modules ## lint swagger files
|
lint-swagger: node_modules
|
||||||
npx spectral lint -q -F hint $(SWAGGER_SPEC)
|
npx spectral lint -q -F hint $(SWAGGER_SPEC)
|
||||||
|
|
||||||
.PHONY: lint-md
|
.PHONY: lint-md
|
||||||
lint-md: node_modules ## lint markdown files
|
lint-md: node_modules
|
||||||
npx markdownlint *.md
|
npx markdownlint docs *.md
|
||||||
|
|
||||||
.PHONY: lint-spell
|
|
||||||
lint-spell: ## lint spelling
|
|
||||||
@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -error $(SPELLCHECK_FILES)
|
|
||||||
|
|
||||||
.PHONY: lint-spell-fix
|
|
||||||
lint-spell-fix: ## lint spelling and fix issues
|
|
||||||
@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -w $(SPELLCHECK_FILES)
|
|
||||||
|
|
||||||
.PHONY: lint-go
|
.PHONY: lint-go
|
||||||
lint-go: ## lint go files
|
lint-go:
|
||||||
$(GO) run $(GOLANGCI_LINT_PACKAGE) run
|
$(GO) run $(GOLANGCI_LINT_PACKAGE) run
|
||||||
|
|
||||||
.PHONY: lint-go-fix
|
.PHONY: lint-go-fix
|
||||||
lint-go-fix: ## lint go files and fix issues
|
lint-go-fix:
|
||||||
$(GO) run $(GOLANGCI_LINT_PACKAGE) run --fix
|
$(GO) run $(GOLANGCI_LINT_PACKAGE) run --fix
|
||||||
|
|
||||||
# workaround step for the lint-go-windows CI task because 'go run' can not
|
# workaround step for the lint-go-windows CI task because 'go run' can not
|
||||||
@@ -364,58 +411,43 @@ lint-go-windows:
|
|||||||
@GOOS= GOARCH= $(GO) install $(GOLANGCI_LINT_PACKAGE)
|
@GOOS= GOARCH= $(GO) install $(GOLANGCI_LINT_PACKAGE)
|
||||||
golangci-lint run
|
golangci-lint run
|
||||||
|
|
||||||
.PHONY: lint-go-gitea-vet
|
.PHONY: lint-go-vet
|
||||||
lint-go-gitea-vet: ## lint go files with gitea-vet
|
lint-go-vet:
|
||||||
@echo "Running gitea-vet..."
|
@echo "Running go vet..."
|
||||||
@GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet
|
@GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet
|
||||||
@$(GO) vet -vettool=gitea-vet ./...
|
@$(GO) vet -vettool=gitea-vet $(GO_PACKAGES)
|
||||||
|
|
||||||
.PHONY: lint-go-gopls
|
|
||||||
lint-go-gopls: ## lint go files with gopls
|
|
||||||
@echo "Running gopls check..."
|
|
||||||
@GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES_NO_BINDATA)
|
|
||||||
|
|
||||||
.PHONY: lint-editorconfig
|
.PHONY: lint-editorconfig
|
||||||
lint-editorconfig:
|
lint-editorconfig:
|
||||||
@echo "Running editorconfig check..."
|
$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) templates .github/workflows
|
||||||
@$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) $(EDITORCONFIG_FILES)
|
|
||||||
|
|
||||||
.PHONY: lint-actions
|
.PHONY: lint-actions
|
||||||
lint-actions: ## lint action workflow files
|
lint-actions:
|
||||||
$(GO) run $(ACTIONLINT_PACKAGE)
|
$(GO) run $(ACTIONLINT_PACKAGE)
|
||||||
|
|
||||||
.PHONY: lint-templates
|
|
||||||
lint-templates: .venv node_modules ## lint template files
|
|
||||||
@node tools/lint-templates-svg.js
|
|
||||||
@poetry run djlint $(shell find templates -type f -iname '*.tmpl')
|
|
||||||
|
|
||||||
.PHONY: lint-yaml
|
|
||||||
lint-yaml: .venv ## lint yaml files
|
|
||||||
@poetry run yamllint -s .
|
|
||||||
|
|
||||||
.PHONY: watch
|
.PHONY: watch
|
||||||
watch: ## watch everything and continuously rebuild
|
watch:
|
||||||
@bash tools/watch.sh
|
@bash build/watch.sh
|
||||||
|
|
||||||
.PHONY: watch-frontend
|
.PHONY: watch-frontend
|
||||||
watch-frontend: node-check node_modules ## watch frontend files and continuously rebuild
|
watch-frontend: node-check node_modules
|
||||||
@rm -rf $(WEBPACK_DEST_ENTRIES)
|
@rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||||
NODE_ENV=development npx webpack --watch --progress
|
NODE_ENV=development npx webpack --watch --progress
|
||||||
|
|
||||||
.PHONY: watch-backend
|
.PHONY: watch-backend
|
||||||
watch-backend: go-check ## watch backend files and continuously rebuild
|
watch-backend: go-check
|
||||||
GITEA_RUN_MODE=dev $(GO) run $(AIR_PACKAGE) -c .air.toml
|
GITEA_RUN_MODE=dev $(GO) run $(AIR_PACKAGE) -c .air.toml
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test: test-frontend test-backend ## test everything
|
test: test-frontend test-backend
|
||||||
|
|
||||||
.PHONY: test-backend
|
.PHONY: test-backend
|
||||||
test-backend: ## test backend files
|
test-backend:
|
||||||
@echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
|
@echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
|
||||||
@$(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_PACKAGES)
|
@$(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_PACKAGES)
|
||||||
|
|
||||||
.PHONY: test-frontend
|
.PHONY: test-frontend
|
||||||
test-frontend: node_modules ## test frontend files
|
test-frontend: node_modules
|
||||||
npx vitest
|
npx vitest
|
||||||
|
|
||||||
.PHONY: test-check
|
.PHONY: test-check
|
||||||
@@ -424,7 +456,7 @@ test-check:
|
|||||||
@diff=$$(git status -s); \
|
@diff=$$(git status -s); \
|
||||||
if [ -n "$$diff" ]; then \
|
if [ -n "$$diff" ]; then \
|
||||||
echo "make test-backend has changed files in the source tree:"; \
|
echo "make test-backend has changed files in the source tree:"; \
|
||||||
printf "%s" "$${diff}"; \
|
echo "$${diff}"; \
|
||||||
echo "You should change the tests to create these files in a temporary directory."; \
|
echo "You should change the tests to create these files in a temporary directory."; \
|
||||||
echo "Do not simply add these files to .gitignore"; \
|
echo "Do not simply add these files to .gitignore"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
@@ -447,7 +479,7 @@ unit-test-coverage:
|
|||||||
@$(GO) test $(GOTESTFLAGS) -timeout=20m -tags='$(TEST_TAGS)' -cover -coverprofile coverage.out $(GO_TEST_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
|
@$(GO) test $(GOTESTFLAGS) -timeout=20m -tags='$(TEST_TAGS)' -cover -coverprofile coverage.out $(GO_TEST_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
|
||||||
|
|
||||||
.PHONY: tidy
|
.PHONY: tidy
|
||||||
tidy: ## run go mod tidy
|
tidy:
|
||||||
$(eval MIN_GO_VERSION := $(shell grep -Eo '^go\s+[0-9]+\.[0-9.]+' go.mod | cut -d' ' -f2))
|
$(eval MIN_GO_VERSION := $(shell grep -Eo '^go\s+[0-9]+\.[0-9.]+' go.mod | cut -d' ' -f2))
|
||||||
$(GO) mod tidy -compat=$(MIN_GO_VERSION)
|
$(GO) mod tidy -compat=$(MIN_GO_VERSION)
|
||||||
@$(MAKE) --no-print-directory $(GO_LICENSE_FILE)
|
@$(MAKE) --no-print-directory $(GO_LICENSE_FILE)
|
||||||
@@ -461,17 +493,15 @@ tidy-check: tidy
|
|||||||
@diff=$$(git diff --color=always go.mod go.sum $(GO_LICENSE_FILE)); \
|
@diff=$$(git diff --color=always go.mod go.sum $(GO_LICENSE_FILE)); \
|
||||||
if [ -n "$$diff" ]; then \
|
if [ -n "$$diff" ]; then \
|
||||||
echo "Please run 'make tidy' and commit the result:"; \
|
echo "Please run 'make tidy' and commit the result:"; \
|
||||||
printf "%s" "$${diff}"; \
|
echo "$${diff}"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
.PHONY: go-licenses
|
.PHONY: go-licenses
|
||||||
go-licenses: $(GO_LICENSE_FILE) ## regenerate go licenses
|
go-licenses: $(GO_LICENSE_FILE)
|
||||||
|
|
||||||
$(GO_LICENSE_FILE): go.mod go.sum
|
$(GO_LICENSE_FILE): go.mod go.sum
|
||||||
@rm -rf $(GO_LICENSE_FILE)
|
-$(GO) run $(GO_LICENSES_PACKAGE) save . --force --save_path=$(GO_LICENSE_TMP_DIR) 2>/dev/null
|
||||||
$(GO) install $(GO_LICENSES_PACKAGE)
|
|
||||||
-GOOS=linux CGO_ENABLED=1 go-licenses save . --force --save_path=$(GO_LICENSE_TMP_DIR) 2>/dev/null
|
|
||||||
$(GO) run build/generate-go-licenses.go $(GO_LICENSE_TMP_DIR) $(GO_LICENSE_FILE)
|
$(GO) run build/generate-go-licenses.go $(GO_LICENSE_TMP_DIR) $(GO_LICENSE_FILE)
|
||||||
@rm -rf $(GO_LICENSE_TMP_DIR)
|
@rm -rf $(GO_LICENSE_TMP_DIR)
|
||||||
|
|
||||||
@@ -513,13 +543,33 @@ test-mysql\#%: integrations.mysql.test generate-ini-mysql
|
|||||||
.PHONY: test-mysql-migration
|
.PHONY: test-mysql-migration
|
||||||
test-mysql-migration: migrations.mysql.test migrations.individual.mysql.test
|
test-mysql-migration: migrations.mysql.test migrations.individual.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' \
|
||||||
|
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||||
|
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
||||||
|
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
|
||||||
|
tests/mysql8.ini.tmpl > tests/mysql8.ini
|
||||||
|
|
||||||
|
.PHONY: test-mysql8
|
||||||
|
test-mysql8: integrations.mysql8.test generate-ini-mysql8
|
||||||
|
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql8.ini ./integrations.mysql8.test
|
||||||
|
|
||||||
|
.PHONY: test-mysql8\#%
|
||||||
|
test-mysql8\#%: integrations.mysql8.test generate-ini-mysql8
|
||||||
|
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql8.ini ./integrations.mysql8.test -test.run $(subst .,/,$*)
|
||||||
|
|
||||||
|
.PHONY: test-mysql8-migration
|
||||||
|
test-mysql8-migration: migrations.mysql8.test migrations.individual.mysql8.test
|
||||||
|
|
||||||
generate-ini-pgsql:
|
generate-ini-pgsql:
|
||||||
sed -e 's|{{TEST_PGSQL_HOST}}|${TEST_PGSQL_HOST}|g' \
|
sed -e 's|{{TEST_PGSQL_HOST}}|${TEST_PGSQL_HOST}|g' \
|
||||||
-e 's|{{TEST_PGSQL_DBNAME}}|${TEST_PGSQL_DBNAME}|g' \
|
-e 's|{{TEST_PGSQL_DBNAME}}|${TEST_PGSQL_DBNAME}|g' \
|
||||||
-e 's|{{TEST_PGSQL_USERNAME}}|${TEST_PGSQL_USERNAME}|g' \
|
-e 's|{{TEST_PGSQL_USERNAME}}|${TEST_PGSQL_USERNAME}|g' \
|
||||||
-e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \
|
-e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \
|
||||||
-e 's|{{TEST_PGSQL_SCHEMA}}|${TEST_PGSQL_SCHEMA}|g' \
|
-e 's|{{TEST_PGSQL_SCHEMA}}|${TEST_PGSQL_SCHEMA}|g' \
|
||||||
-e 's|{{TEST_MINIO_ENDPOINT}}|${TEST_MINIO_ENDPOINT}|g' \
|
|
||||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||||
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
||||||
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
|
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
|
||||||
@@ -558,7 +608,8 @@ test-mssql\#%: integrations.mssql.test generate-ini-mssql
|
|||||||
test-mssql-migration: migrations.mssql.test migrations.individual.mssql.test
|
test-mssql-migration: migrations.mssql.test migrations.individual.mssql.test
|
||||||
|
|
||||||
.PHONY: playwright
|
.PHONY: playwright
|
||||||
playwright: deps-frontend
|
playwright: $(PLAYWRIGHT_DIR)
|
||||||
|
npm install --no-save @playwright/test
|
||||||
npx playwright install $(PLAYWRIGHT_FLAGS)
|
npx playwright install $(PLAYWRIGHT_FLAGS)
|
||||||
|
|
||||||
.PHONY: test-e2e%
|
.PHONY: test-e2e%
|
||||||
@@ -585,6 +636,14 @@ test-e2e-mysql: playwright e2e.mysql.test generate-ini-mysql
|
|||||||
test-e2e-mysql\#%: playwright e2e.mysql.test generate-ini-mysql
|
test-e2e-mysql\#%: playwright e2e.mysql.test generate-ini-mysql
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./e2e.mysql.test -test.run TestE2e/$*
|
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./e2e.mysql.test -test.run TestE2e/$*
|
||||||
|
|
||||||
|
.PHONY: test-e2e-mysql8
|
||||||
|
test-e2e-mysql8: playwright e2e.mysql8.test generate-ini-mysql8
|
||||||
|
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql8.ini ./e2e.mysql8.test
|
||||||
|
|
||||||
|
.PHONY: test-e2e-mysql8\#%
|
||||||
|
test-e2e-mysql8\#%: playwright e2e.mysql8.test generate-ini-mysql8
|
||||||
|
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql8.ini ./e2e.mysql8.test -test.run TestE2e/$*
|
||||||
|
|
||||||
.PHONY: test-e2e-pgsql
|
.PHONY: test-e2e-pgsql
|
||||||
test-e2e-pgsql: playwright e2e.pgsql.test generate-ini-pgsql
|
test-e2e-pgsql: playwright e2e.pgsql.test generate-ini-pgsql
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./e2e.pgsql.test
|
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./e2e.pgsql.test
|
||||||
@@ -628,6 +687,9 @@ integration-test-coverage-sqlite: integrations.cover.sqlite.test generate-ini-sq
|
|||||||
integrations.mysql.test: git-check $(GO_SOURCES)
|
integrations.mysql.test: git-check $(GO_SOURCES)
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.mysql.test
|
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.mysql.test
|
||||||
|
|
||||||
|
integrations.mysql8.test: git-check $(GO_SOURCES)
|
||||||
|
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.mysql8.test
|
||||||
|
|
||||||
integrations.pgsql.test: git-check $(GO_SOURCES)
|
integrations.pgsql.test: git-check $(GO_SOURCES)
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.pgsql.test
|
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.pgsql.test
|
||||||
|
|
||||||
@@ -648,6 +710,11 @@ migrations.mysql.test: $(GO_SOURCES) generate-ini-mysql
|
|||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.mysql.test
|
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.mysql.test
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./migrations.mysql.test
|
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./migrations.mysql.test
|
||||||
|
|
||||||
|
.PHONY: migrations.mysql8.test
|
||||||
|
migrations.mysql8.test: $(GO_SOURCES) generate-ini-mysql8
|
||||||
|
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.mysql8.test
|
||||||
|
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql8.ini ./migrations.mysql8.test
|
||||||
|
|
||||||
.PHONY: migrations.pgsql.test
|
.PHONY: migrations.pgsql.test
|
||||||
migrations.pgsql.test: $(GO_SOURCES) generate-ini-pgsql
|
migrations.pgsql.test: $(GO_SOURCES) generate-ini-pgsql
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.pgsql.test
|
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.pgsql.test
|
||||||
@@ -665,23 +732,36 @@ migrations.sqlite.test: $(GO_SOURCES) generate-ini-sqlite
|
|||||||
|
|
||||||
.PHONY: migrations.individual.mysql.test
|
.PHONY: migrations.individual.mysql.test
|
||||||
migrations.individual.mysql.test: $(GO_SOURCES)
|
migrations.individual.mysql.test: $(GO_SOURCES)
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini $(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -p 1 $(MIGRATE_TEST_PACKAGES)
|
for pkg in $(shell $(GO) list code.gitea.io/gitea/models/migrations/...); do \
|
||||||
|
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' $$pkg; \
|
||||||
|
done
|
||||||
|
|
||||||
.PHONY: migrations.individual.sqlite.test\#%
|
.PHONY: migrations.individual.mysql8.test
|
||||||
|
migrations.individual.mysql8.test: $(GO_SOURCES)
|
||||||
|
for pkg in $(shell $(GO) list code.gitea.io/gitea/models/migrations/...); do \
|
||||||
|
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql8.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' $$pkg; \
|
||||||
|
done
|
||||||
|
|
||||||
|
.PHONY: migrations.individual.mysql8.test\#%
|
||||||
migrations.individual.sqlite.test\#%: $(GO_SOURCES) generate-ini-sqlite
|
migrations.individual.sqlite.test\#%: $(GO_SOURCES) generate-ini-sqlite
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$*
|
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$*
|
||||||
|
|
||||||
.PHONY: migrations.individual.pgsql.test
|
.PHONY: migrations.individual.pgsql.test
|
||||||
migrations.individual.pgsql.test: $(GO_SOURCES)
|
migrations.individual.pgsql.test: $(GO_SOURCES)
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini $(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -p 1 $(MIGRATE_TEST_PACKAGES)
|
for pkg in $(shell $(GO) list code.gitea.io/gitea/models/migrations/...); do \
|
||||||
|
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' $$pkg; \
|
||||||
|
done
|
||||||
|
|
||||||
.PHONY: migrations.individual.pgsql.test\#%
|
.PHONY: migrations.individual.pgsql.test\#%
|
||||||
migrations.individual.pgsql.test\#%: $(GO_SOURCES) generate-ini-pgsql
|
migrations.individual.pgsql.test\#%: $(GO_SOURCES) generate-ini-pgsql
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$*
|
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$*
|
||||||
|
|
||||||
|
|
||||||
.PHONY: migrations.individual.mssql.test
|
.PHONY: migrations.individual.mssql.test
|
||||||
migrations.individual.mssql.test: $(GO_SOURCES) generate-ini-mssql
|
migrations.individual.mssql.test: $(GO_SOURCES) generate-ini-mssql
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini $(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -p 1 $(MIGRATE_TEST_PACKAGES)
|
for pkg in $(shell $(GO) list code.gitea.io/gitea/models/migrations/...); do \
|
||||||
|
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' $$pkg -test.failfast; \
|
||||||
|
done
|
||||||
|
|
||||||
.PHONY: migrations.individual.mssql.test\#%
|
.PHONY: migrations.individual.mssql.test\#%
|
||||||
migrations.individual.mssql.test\#%: $(GO_SOURCES) generate-ini-mssql
|
migrations.individual.mssql.test\#%: $(GO_SOURCES) generate-ini-mssql
|
||||||
@@ -689,7 +769,9 @@ migrations.individual.mssql.test\#%: $(GO_SOURCES) generate-ini-mssql
|
|||||||
|
|
||||||
.PHONY: migrations.individual.sqlite.test
|
.PHONY: migrations.individual.sqlite.test
|
||||||
migrations.individual.sqlite.test: $(GO_SOURCES) generate-ini-sqlite
|
migrations.individual.sqlite.test: $(GO_SOURCES) generate-ini-sqlite
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini $(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -p 1 $(MIGRATE_TEST_PACKAGES)
|
for pkg in $(shell $(GO) list code.gitea.io/gitea/models/migrations/...); do \
|
||||||
|
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' $$pkg; \
|
||||||
|
done
|
||||||
|
|
||||||
.PHONY: migrations.individual.sqlite.test\#%
|
.PHONY: migrations.individual.sqlite.test\#%
|
||||||
migrations.individual.sqlite.test\#%: $(GO_SOURCES) generate-ini-sqlite
|
migrations.individual.sqlite.test\#%: $(GO_SOURCES) generate-ini-sqlite
|
||||||
@@ -698,6 +780,9 @@ migrations.individual.sqlite.test\#%: $(GO_SOURCES) generate-ini-sqlite
|
|||||||
e2e.mysql.test: $(GO_SOURCES)
|
e2e.mysql.test: $(GO_SOURCES)
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.mysql.test
|
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.mysql.test
|
||||||
|
|
||||||
|
e2e.mysql8.test: $(GO_SOURCES)
|
||||||
|
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.mysql8.test
|
||||||
|
|
||||||
e2e.pgsql.test: $(GO_SOURCES)
|
e2e.pgsql.test: $(GO_SOURCES)
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.pgsql.test
|
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.pgsql.test
|
||||||
|
|
||||||
@@ -715,17 +800,17 @@ install: $(wildcard *.go)
|
|||||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
|
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build: frontend backend ## build everything
|
build: frontend backend
|
||||||
|
|
||||||
.PHONY: frontend
|
.PHONY: frontend
|
||||||
frontend: $(WEBPACK_DEST) ## build frontend files
|
frontend: $(WEBPACK_DEST)
|
||||||
|
|
||||||
.PHONY: backend
|
.PHONY: backend
|
||||||
backend: go-check generate-backend $(EXECUTABLE) ## build backend files
|
backend: go-check generate-backend $(EXECUTABLE)
|
||||||
|
|
||||||
# We generate the backend before the frontend in case we in future we want to generate things in the frontend from generated files in backend
|
# We generate the backend before the frontend in case we in future we want to generate things in the frontend from generated files in backend
|
||||||
.PHONY: generate
|
.PHONY: generate
|
||||||
generate: generate-backend ## run "go generate"
|
generate: generate-backend
|
||||||
|
|
||||||
.PHONY: generate-backend
|
.PHONY: generate-backend
|
||||||
generate-backend: $(TAGS_PREREQ) generate-go
|
generate-backend: $(TAGS_PREREQ) generate-go
|
||||||
@@ -733,39 +818,51 @@ generate-backend: $(TAGS_PREREQ) generate-go
|
|||||||
.PHONY: generate-go
|
.PHONY: generate-go
|
||||||
generate-go: $(TAGS_PREREQ)
|
generate-go: $(TAGS_PREREQ)
|
||||||
@echo "Running go generate..."
|
@echo "Running go generate..."
|
||||||
@CC= GOOS= GOARCH= CGO_ENABLED=0 $(GO) generate -tags '$(TAGS)' ./...
|
@CC= GOOS= GOARCH= $(GO) generate -tags '$(TAGS)' $(GO_PACKAGES)
|
||||||
|
|
||||||
.PHONY: security-check
|
.PHONY: security-check
|
||||||
security-check:
|
security-check:
|
||||||
go run $(GOVULNCHECK_PACKAGE) -show color ./...
|
go run $(GOVULNCHECK_PACKAGE) ./...
|
||||||
|
|
||||||
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
|
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
|
||||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||||
|
|
||||||
.PHONY: release
|
.PHONY: release
|
||||||
release: frontend generate release-windows release-linux release-darwin release-freebsd release-copy release-compress vendor release-sources release-check
|
release: frontend generate release-windows release-linux release-darwin release-freebsd release-copy release-compress vendor release-sources release-docs release-check
|
||||||
|
|
||||||
$(DIST_DIRS):
|
$(DIST_DIRS):
|
||||||
mkdir -p $(DIST_DIRS)
|
mkdir -p $(DIST_DIRS)
|
||||||
|
|
||||||
.PHONY: release-windows
|
.PHONY: release-windows
|
||||||
release-windows: | $(DIST_DIRS)
|
release-windows: | $(DIST_DIRS)
|
||||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||||
ifeq (,$(findstring gogit,$(TAGS)))
|
ifeq (,$(findstring gogit,$(TAGS)))
|
||||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo gogit $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
|
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo gogit $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
|
||||||
|
endif
|
||||||
|
ifneq ($(DRONE_TAG),)
|
||||||
|
cp /build/* $(DIST)/binaries
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: release-linux
|
.PHONY: release-linux
|
||||||
release-linux: | $(DIST_DIRS)
|
release-linux: | $(DIST_DIRS)
|
||||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
|
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
|
||||||
|
ifneq ($(DRONE_TAG),)
|
||||||
|
cp /build/* $(DIST)/binaries
|
||||||
|
endif
|
||||||
|
|
||||||
.PHONY: release-darwin
|
.PHONY: release-darwin
|
||||||
release-darwin: | $(DIST_DIRS)
|
release-darwin: | $(DIST_DIRS)
|
||||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w $(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
|
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
|
||||||
|
ifneq ($(DRONE_TAG),)
|
||||||
|
cp /build/* $(DIST)/binaries
|
||||||
|
endif
|
||||||
|
|
||||||
.PHONY: release-freebsd
|
.PHONY: release-freebsd
|
||||||
release-freebsd: | $(DIST_DIRS)
|
release-freebsd: | $(DIST_DIRS)
|
||||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w $(LDFLAGS)' -targets 'freebsd/amd64' -out gitea-$(VERSION) .
|
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'freebsd/amd64' -out gitea-$(VERSION) .
|
||||||
|
ifneq ($(DRONE_TAG),)
|
||||||
|
cp /build/* $(DIST)/binaries
|
||||||
|
endif
|
||||||
|
|
||||||
.PHONY: release-copy
|
.PHONY: release-copy
|
||||||
release-copy: | $(DIST_DIRS)
|
release-copy: | $(DIST_DIRS)
|
||||||
@@ -777,7 +874,7 @@ release-check: | $(DIST_DIRS)
|
|||||||
|
|
||||||
.PHONY: release-compress
|
.PHONY: release-compress
|
||||||
release-compress: | $(DIST_DIRS)
|
release-compress: | $(DIST_DIRS)
|
||||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && $(GO) run $(GXZ_PACKAGE) -k -9 $${file}; done;
|
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && $(GO) run $(GXZ_PAGAGE) -k -9 $${file}; done;
|
||||||
|
|
||||||
.PHONY: release-sources
|
.PHONY: release-sources
|
||||||
release-sources: | $(DIST_DIRS)
|
release-sources: | $(DIST_DIRS)
|
||||||
@@ -789,76 +886,74 @@ release-sources: | $(DIST_DIRS)
|
|||||||
tar $(addprefix $(EXCL),$(TAR_EXCLUDES)) $(TRANSFORM) -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz .
|
tar $(addprefix $(EXCL),$(TAR_EXCLUDES)) $(TRANSFORM) -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz .
|
||||||
rm -f $(STORED_VERSION_FILE)
|
rm -f $(STORED_VERSION_FILE)
|
||||||
|
|
||||||
.PHONY: deps
|
.PHONY: release-docs
|
||||||
deps: deps-frontend deps-backend deps-tools deps-py ## install dependencies
|
release-docs: | $(DIST_DIRS) docs
|
||||||
|
tar -czf $(DIST)/release/gitea-docs-$(VERSION).tar.gz -C ./docs .
|
||||||
|
|
||||||
.PHONY: deps-py
|
.PHONY: docs
|
||||||
deps-py: .venv ## install python dependencies
|
docs:
|
||||||
|
cd docs; bash scripts/trans-copy.sh;
|
||||||
|
|
||||||
|
.PHONY: deps
|
||||||
|
deps: deps-frontend deps-backend deps-tools
|
||||||
|
|
||||||
.PHONY: deps-frontend
|
.PHONY: deps-frontend
|
||||||
deps-frontend: node_modules ## install frontend dependencies
|
deps-frontend: node_modules
|
||||||
|
|
||||||
.PHONY: deps-backend
|
.PHONY: deps-backend
|
||||||
deps-backend: ## install backend dependencies
|
deps-backend:
|
||||||
$(GO) mod download
|
$(GO) mod download
|
||||||
|
|
||||||
.PHONY: deps-tools
|
.PHONY: deps-tools
|
||||||
deps-tools: ## install tool dependencies
|
deps-tools:
|
||||||
$(GO) install $(AIR_PACKAGE) & \
|
$(GO) install $(AIR_PACKAGE)
|
||||||
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE) & \
|
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE)
|
||||||
$(GO) install $(GOFUMPT_PACKAGE) & \
|
$(GO) install $(GOFUMPT_PACKAGE)
|
||||||
$(GO) install $(GOLANGCI_LINT_PACKAGE) & \
|
$(GO) install $(GOLANGCI_LINT_PACKAGE)
|
||||||
$(GO) install $(GXZ_PACKAGE) & \
|
$(GO) install $(GXZ_PAGAGE)
|
||||||
$(GO) install $(MISSPELL_PACKAGE) & \
|
$(GO) install $(MISSPELL_PACKAGE)
|
||||||
$(GO) install $(SWAGGER_PACKAGE) & \
|
$(GO) install $(SWAGGER_PACKAGE)
|
||||||
$(GO) install $(XGO_PACKAGE) & \
|
$(GO) install $(XGO_PACKAGE)
|
||||||
$(GO) install $(GO_LICENSES_PACKAGE) & \
|
$(GO) install $(GO_LICENSES_PACKAGE)
|
||||||
$(GO) install $(GOVULNCHECK_PACKAGE) & \
|
$(GO) install $(GOVULNCHECK_PACKAGE)
|
||||||
$(GO) install $(ACTIONLINT_PACKAGE) & \
|
$(GO) install $(ACTIONLINT_PACKAGE)
|
||||||
$(GO) install $(GOPLS_PACKAGE) & \
|
|
||||||
wait
|
|
||||||
|
|
||||||
node_modules: package-lock.json
|
node_modules: package-lock.json
|
||||||
npm install --no-save
|
npm install --no-save
|
||||||
@touch node_modules
|
@touch node_modules
|
||||||
|
|
||||||
.venv: poetry.lock
|
.PHONY: npm-update
|
||||||
poetry install
|
npm-update: node-check | node_modules
|
||||||
@touch .venv
|
npx updates -cu
|
||||||
|
|
||||||
.PHONY: update
|
|
||||||
update: update-js update-py ## update js and py dependencies
|
|
||||||
|
|
||||||
.PHONY: update-js
|
|
||||||
update-js: node-check | node_modules ## update js dependencies
|
|
||||||
npx updates -u -f package.json
|
|
||||||
rm -rf node_modules package-lock.json
|
rm -rf node_modules package-lock.json
|
||||||
npm install --package-lock
|
npm install --package-lock
|
||||||
npx nolyfill install
|
|
||||||
npm install --package-lock
|
|
||||||
@touch node_modules
|
@touch node_modules
|
||||||
|
|
||||||
.PHONY: update-py
|
.PHONY: fomantic
|
||||||
update-py: node-check | node_modules ## update py dependencies
|
fomantic:
|
||||||
npx updates -u -f pyproject.toml
|
rm -rf $(FOMANTIC_WORK_DIR)/build
|
||||||
rm -rf .venv poetry.lock
|
cd $(FOMANTIC_WORK_DIR) && npm install --no-save
|
||||||
poetry install
|
cp -f $(FOMANTIC_WORK_DIR)/theme.config.less $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/theme.config
|
||||||
@touch .venv
|
cp -rf $(FOMANTIC_WORK_DIR)/_site $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/
|
||||||
|
cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build
|
||||||
|
# fomantic uses "touchstart" as click event for some browsers, it's not ideal, so we force fomantic to always use "click" as click event
|
||||||
|
$(SED_INPLACE) -e 's/clickEvent[ \t]*=/clickEvent = "click", unstableClickEvent =/g' $(FOMANTIC_WORK_DIR)/build/semantic.js
|
||||||
|
$(SED_INPLACE) -e 's/\r//g' $(FOMANTIC_WORK_DIR)/build/semantic.css $(FOMANTIC_WORK_DIR)/build/semantic.js
|
||||||
|
rm -f $(FOMANTIC_WORK_DIR)/build/*.min.*
|
||||||
|
|
||||||
.PHONY: webpack
|
.PHONY: webpack
|
||||||
webpack: $(WEBPACK_DEST) ## build webpack files
|
webpack: $(WEBPACK_DEST)
|
||||||
|
|
||||||
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
|
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
|
||||||
@$(MAKE) -s node-check node_modules
|
@$(MAKE) -s node-check node_modules
|
||||||
@rm -rf $(WEBPACK_DEST_ENTRIES)
|
rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||||
@echo "Running webpack..."
|
npx webpack
|
||||||
@BROWSERSLIST_IGNORE_OLD_DATA=true npx webpack
|
|
||||||
@touch $(WEBPACK_DEST)
|
@touch $(WEBPACK_DEST)
|
||||||
|
|
||||||
.PHONY: svg
|
.PHONY: svg
|
||||||
svg: node-check | node_modules ## build svg files
|
svg: node-check | node_modules
|
||||||
rm -rf $(SVG_DEST_DIR)
|
rm -rf $(SVG_DEST_DIR)
|
||||||
node tools/generate-svg.js
|
node build/generate-svg.js
|
||||||
|
|
||||||
.PHONY: svg-check
|
.PHONY: svg-check
|
||||||
svg-check: svg
|
svg-check: svg
|
||||||
@@ -866,7 +961,7 @@ svg-check: svg
|
|||||||
@diff=$$(git diff --color=always --cached $(SVG_DEST_DIR)); \
|
@diff=$$(git diff --color=always --cached $(SVG_DEST_DIR)); \
|
||||||
if [ -n "$$diff" ]; then \
|
if [ -n "$$diff" ]; then \
|
||||||
echo "Please run 'make svg' and 'git add $(SVG_DEST_DIR)' and commit the result:"; \
|
echo "Please run 'make svg' and 'git add $(SVG_DEST_DIR)' and commit the result:"; \
|
||||||
printf "%s" "$${diff}"; \
|
echo "$${diff}"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -877,7 +972,7 @@ lockfile-check:
|
|||||||
if [ -n "$$diff" ]; then \
|
if [ -n "$$diff" ]; then \
|
||||||
echo "package-lock.json is inconsistent with package.json"; \
|
echo "package-lock.json is inconsistent with package.json"; \
|
||||||
echo "Please run 'npm install --package-lock-only' and commit the result:"; \
|
echo "Please run 'npm install --package-lock-only' and commit the result:"; \
|
||||||
printf "%s" "$${diff}"; \
|
echo "$${diff}"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -891,17 +986,21 @@ update-translations:
|
|||||||
mv ./translations/*.ini ./options/locale/
|
mv ./translations/*.ini ./options/locale/
|
||||||
rmdir ./translations
|
rmdir ./translations
|
||||||
|
|
||||||
|
.PHONY: generate-license
|
||||||
|
generate-license:
|
||||||
|
$(GO) run build/generate-licenses.go
|
||||||
|
|
||||||
.PHONY: generate-gitignore
|
.PHONY: generate-gitignore
|
||||||
generate-gitignore: ## update gitignore files
|
generate-gitignore:
|
||||||
$(GO) run build/generate-gitignores.go
|
$(GO) run build/generate-gitignores.go
|
||||||
|
|
||||||
.PHONY: generate-images
|
.PHONY: generate-images
|
||||||
generate-images: | node_modules
|
generate-images: | node_modules
|
||||||
npm install --no-save fabric@6 imagemin-zopfli@7
|
npm install --no-save --no-package-lock fabric@5 imagemin-zopfli@7
|
||||||
node tools/generate-images.js $(TAGS)
|
node build/generate-images.js $(TAGS)
|
||||||
|
|
||||||
.PHONY: generate-manpage
|
.PHONY: generate-manpage
|
||||||
generate-manpage: ## generate manpage
|
generate-manpage:
|
||||||
@[ -f gitea ] || make backend
|
@[ -f gitea ] || make backend
|
||||||
@mkdir -p man/man1/ man/man5
|
@mkdir -p man/man1/ man/man5
|
||||||
@./gitea docs --man > man/man1/gitea.1
|
@./gitea docs --man > man/man1/gitea.1
|
||||||
@@ -915,8 +1014,3 @@ docker:
|
|||||||
|
|
||||||
# This endif closes the if at the top of the file
|
# This endif closes the if at the top of the file
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Disable parallel execution because it would break some targets that don't
|
|
||||||
# specify exact dependencies like 'backend' which does currently not depend
|
|
||||||
# on 'frontend' to enable Node.js-less builds from source tarballs.
|
|
||||||
.NOTPARALLEL:
|
|
||||||
|
|||||||
201
README.md
201
README.md
@@ -1,17 +1,58 @@
|
|||||||
# Gitea
|
<p align="center">
|
||||||
|
<a href="https://gitea.io/">
|
||||||
|
<img alt="Gitea" src="https://raw.githubusercontent.com/go-gitea/gitea/main/public/img/gitea.svg" width="220"/>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<h1 align="center">Gitea - Git with a cup of tea</h1>
|
||||||
|
|
||||||
[](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
|
<p align="center">
|
||||||
[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
|
<a href="https://drone.gitea.io/go-gitea/gitea" title="Build Status">
|
||||||
[](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
|
<img src="https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg?ref=refs/heads/main">
|
||||||
[](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
|
</a>
|
||||||
[](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
|
<a href="https://discord.gg/Gitea" title="Join the Discord chat at https://discord.gg/Gitea">
|
||||||
[](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
|
<img src="https://img.shields.io/discord/322538954119184384.svg">
|
||||||
[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
|
</a>
|
||||||
[](https://opensource.org/licenses/MIT "License: MIT")
|
<a href="https://app.codecov.io/gh/go-gitea/gitea" title="Codecov">
|
||||||
[](https://gitpod.io/#https://github.com/go-gitea/gitea)
|
<img src="https://codecov.io/gh/go-gitea/gitea/branch/main/graph/badge.svg">
|
||||||
[](https://translate.gitea.com "Crowdin")
|
</a>
|
||||||
|
<a href="https://goreportcard.com/report/code.gitea.io/gitea" title="Go Report Card">
|
||||||
|
<img src="https://goreportcard.com/badge/code.gitea.io/gitea">
|
||||||
|
</a>
|
||||||
|
<a href="https://pkg.go.dev/code.gitea.io/gitea" title="GoDoc">
|
||||||
|
<img src="https://pkg.go.dev/badge/code.gitea.io/gitea?status.svg">
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/go-gitea/gitea/releases/latest" title="GitHub release">
|
||||||
|
<img src="https://img.shields.io/github/release/go-gitea/gitea.svg">
|
||||||
|
</a>
|
||||||
|
<a href="https://www.codetriage.com/go-gitea/gitea" title="Help Contribute to Open Source">
|
||||||
|
<img src="https://www.codetriage.com/go-gitea/gitea/badges/users.svg">
|
||||||
|
</a>
|
||||||
|
<a href="https://opencollective.com/gitea" title="Become a backer/sponsor of gitea">
|
||||||
|
<img src="https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen">
|
||||||
|
</a>
|
||||||
|
<a href="https://opensource.org/licenses/MIT" title="License: MIT">
|
||||||
|
<img src="https://img.shields.io/badge/License-MIT-blue.svg">
|
||||||
|
</a>
|
||||||
|
<a href="https://gitpod.io/#https://github.com/go-gitea/gitea">
|
||||||
|
<img
|
||||||
|
src="https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod"
|
||||||
|
alt="Contribute with Gitpod"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="https://crowdin.com/project/gitea" title="Crowdin">
|
||||||
|
<img src="https://badges.crowdin.net/gitea/localized.svg">
|
||||||
|
</a>
|
||||||
|
<a href="https://www.tickgit.com/browse?repo=github.com/go-gitea/gitea&branch=main" title="TODOs">
|
||||||
|
<img src="https://badgen.net/https/api.tickgit.com/badgen/github.com/go-gitea/gitea/main">
|
||||||
|
</a>
|
||||||
|
<a href="https://app.bountysource.com/teams/gitea" title="Bountysource">
|
||||||
|
<img src="https://img.shields.io/bountysource/team/gitea/activity">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
[繁體中文](./README.zh-tw.md) | [简体中文](./README.zh-cn.md)
|
<p align="center">
|
||||||
|
<a href="README_ZH.md">View this document in Chinese</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
@@ -21,24 +62,11 @@ painless way of setting up a self-hosted Git service.
|
|||||||
As Gitea is written in Go, it works across **all** the platforms and
|
As Gitea is written in Go, it works across **all** the platforms and
|
||||||
architectures that are supported by Go, including Linux, macOS, and
|
architectures that are supported by Go, including Linux, macOS, and
|
||||||
Windows on x86, amd64, ARM and PowerPC architectures.
|
Windows on x86, amd64, ARM and PowerPC architectures.
|
||||||
|
You can try it out using [the online demo](https://try.gitea.io/).
|
||||||
This project has been
|
This project has been
|
||||||
[forked](https://blog.gitea.com/welcome-to-gitea/) from
|
[forked](https://blog.gitea.io/2016/12/welcome-to-gitea/) from
|
||||||
[Gogs](https://gogs.io) since November of 2016, but a lot has changed.
|
[Gogs](https://gogs.io) since November of 2016, but a lot has changed.
|
||||||
|
|
||||||
For online demonstrations, you can visit [demo.gitea.com](https://demo.gitea.com).
|
|
||||||
|
|
||||||
For accessing free Gitea service (with a limited number of repositories), you can visit [gitea.com](https://gitea.com/user/login).
|
|
||||||
|
|
||||||
To quickly deploy your own dedicated Gitea instance on Gitea Cloud, you can start a free trial at [cloud.gitea.com](https://cloud.gitea.com).
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
You can find comprehensive documentation on our official [documentation website](https://docs.gitea.com/).
|
|
||||||
|
|
||||||
It includes installation, administration, usage, development, contributing guides, and more to help you get started and explore all features effectively.
|
|
||||||
|
|
||||||
If you have any suggestions or would like to contribute to it, you can visit the [documentation repository](https://gitea.com/gitea/docs)
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
From the root of the source tree, run:
|
From the root of the source tree, run:
|
||||||
@@ -51,52 +79,51 @@ or if SQLite support is required:
|
|||||||
|
|
||||||
The `build` target is split into two sub-targets:
|
The `build` target is split into two sub-targets:
|
||||||
|
|
||||||
- `make backend` which requires [Go Stable](https://go.dev/dl/), the required version is defined in [go.mod](/go.mod).
|
- `make backend` which requires [Go Stable](https://go.dev/dl/), required version is defined in [go.mod](/go.mod).
|
||||||
- `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater.
|
- `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater and Internet connectivity to download npm dependencies.
|
||||||
|
|
||||||
Internet connectivity is required to download the go and npm modules. When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js.
|
When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js and Internet connectivity.
|
||||||
|
|
||||||
More info: https://docs.gitea.com/installation/install-from-source
|
Parallelism (`make -j <num>`) is not supported.
|
||||||
|
|
||||||
|
More info: https://docs.gitea.io/en-us/install-from-source/
|
||||||
|
|
||||||
## Using
|
## Using
|
||||||
|
|
||||||
After building, a binary file named `gitea` will be generated in the root of the source tree by default. To run it, use:
|
|
||||||
|
|
||||||
./gitea web
|
./gitea web
|
||||||
|
|
||||||
> [!NOTE]
|
NOTE: If you're interested in using our APIs, we have experimental
|
||||||
> If you're interested in using our APIs, we have experimental support with [documentation](https://docs.gitea.com/api).
|
support with [documentation](https://try.gitea.io/api/swagger).
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Expected workflow is: Fork -> Patch -> Push -> Pull Request
|
Expected workflow is: Fork -> Patch -> Push -> Pull Request
|
||||||
|
|
||||||
> [!NOTE]
|
NOTES:
|
||||||
>
|
|
||||||
> 1. **YOU MUST READ THE [CONTRIBUTORS GUIDE](CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST.**
|
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!
|
2. If you have found a vulnerability in the project, please write privately to **security@gitea.io**. Thanks!
|
||||||
|
|
||||||
## Translating
|
## Translating
|
||||||
|
|
||||||
[](https://translate.gitea.com)
|
Translations are done through Crowdin. If you want to translate to a new language ask one of the managers in the Crowdin project to add a new language there.
|
||||||
|
|
||||||
Translations are done through [Crowdin](https://translate.gitea.com). If you want to translate to a new language ask one of the managers in the Crowdin project to add a new language there.
|
|
||||||
|
|
||||||
You can also just create an issue for adding a language or ask on discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty but we hope to fill it as questions pop up.
|
You can also just create an issue for adding a language or ask on discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty but we hope to fill it as questions pop up.
|
||||||
|
|
||||||
Get more information from [documentation](https://docs.gitea.com/contributing/localization).
|
https://docs.gitea.io/en-us/contributing/translation-guidelines/
|
||||||
|
|
||||||
## Official and Third-Party Projects
|
[](https://crowdin.com/project/gitea)
|
||||||
|
|
||||||
We provide an official [go-sdk](https://gitea.com/gitea/go-sdk), a CLI tool called [tea](https://gitea.com/gitea/tea) and an [action runner](https://gitea.com/gitea/act_runner) for Gitea Action.
|
## Further information
|
||||||
|
|
||||||
We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea), where you can discover more third-party projects, including SDKs, plugins, themes, and more.
|
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/Gitea) or create a post in the [discourse forum](https://discourse.gitea.io/).
|
||||||
|
|
||||||
## Communication
|
We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea).
|
||||||
|
|
||||||
[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
|
The Hugo-based documentation theme is hosted at [gitea/theme](https://gitea.com/gitea/theme).
|
||||||
|
|
||||||
If you have questions that are not covered by the [documentation](https://docs.gitea.com/), you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create a post in the [discourse forum](https://forum.gitea.com/).
|
The official Gitea CLI is developed at [gitea/tea](https://gitea.com/gitea/tea).
|
||||||
|
|
||||||
## Authors
|
## Authors
|
||||||
|
|
||||||
@@ -124,6 +151,7 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
|
|||||||
<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/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/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>
|
<a href="https://opencollective.com/gitea/sponsor/9/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/9/avatar.svg"></a>
|
||||||
|
<a href="https://cynkra.com/" target="_blank"><img src="https://images.opencollective.com/cynkra/logo/square/64/192.png"></a>
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
@@ -135,79 +163,18 @@ Gitea is pronounced [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY) as in "gi-tea"
|
|||||||
|
|
||||||
We're [working on it](https://github.com/go-gitea/gitea/issues/1029).
|
We're [working on it](https://github.com/go-gitea/gitea/issues/1029).
|
||||||
|
|
||||||
**Where can I find the security patches?**
|
|
||||||
|
|
||||||
In the [release log](https://github.com/go-gitea/gitea/releases) or the [change log](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md), search for the keyword `SECURITY` to find the security patches.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the MIT License.
|
This project is licensed under the MIT License.
|
||||||
See the [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) file
|
See the [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) file
|
||||||
for the full license text.
|
for the full license text.
|
||||||
|
|
||||||
## Further information
|
## Screenshots
|
||||||
|
|
||||||
<details>
|
Looking for an overview of the interface? Check it out!
|
||||||
<summary>Looking for an overview of the interface? Check it out!</summary>
|
|
||||||
|
|
||||||
### Login/Register Page
|
||||
|
||||||
|
|:---:|:---:|:---:|
|
||||||

|
||||
|
||||||

|
|||
|
||||||
|
|||
|
||||||
### User Dashboard
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
### User Profile
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Explore
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
### Repository
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
#### Repository Issue
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
#### Repository Pull Requests
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
#### Repository Actions
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
#### Repository Activity
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
### Organization
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|||||||
206
README.zh-cn.md
206
README.zh-cn.md
@@ -1,206 +0,0 @@
|
|||||||
# Gitea
|
|
||||||
|
|
||||||
[](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
|
|
||||||
[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
|
|
||||||
[](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
|
|
||||||
[](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
|
|
||||||
[](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
|
|
||||||
[](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
|
|
||||||
[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
|
|
||||||
[](https://opensource.org/licenses/MIT "License: MIT")
|
|
||||||
[](https://gitpod.io/#https://github.com/go-gitea/gitea)
|
|
||||||
[](https://translate.gitea.com "Crowdin")
|
|
||||||
|
|
||||||
[English](./README.md) | [繁體中文](./README.zh-tw.md)
|
|
||||||
|
|
||||||
## 目的
|
|
||||||
|
|
||||||
这个项目的目标是提供最简单、最快速、最无痛的方式来设置自托管的 Git 服务。
|
|
||||||
|
|
||||||
由于 Gitea 是用 Go 语言编写的,它可以在 Go 支持的所有平台和架构上运行,包括 Linux、macOS 和 Windows 的 x86、amd64、ARM 和 PowerPC 架构。这个项目自 2016 年 11 月从 [Gogs](https://gogs.io) [分叉](https://blog.gitea.com/welcome-to-gitea/) 而来,但已经有了很多变化。
|
|
||||||
|
|
||||||
在线演示可以访问 [demo.gitea.com](https://demo.gitea.com)。
|
|
||||||
|
|
||||||
要访问免费的 Gitea 服务(有一定数量的仓库限制),可以访问 [gitea.com](https://gitea.com/user/login)。
|
|
||||||
|
|
||||||
要快速部署您自己的专用 Gitea 实例,可以在 [cloud.gitea.com](https://cloud.gitea.com) 开始免费试用。
|
|
||||||
|
|
||||||
## 文件
|
|
||||||
|
|
||||||
您可以在我们的官方 [文件网站](https://docs.gitea.com/) 上找到全面的文件。
|
|
||||||
|
|
||||||
它包括安装、管理、使用、开发、贡献指南等,帮助您快速入门并有效地探索所有功能。
|
|
||||||
|
|
||||||
如果您有任何建议或想要贡献,可以访问 [文件仓库](https://gitea.com/gitea/docs)
|
|
||||||
|
|
||||||
## 构建
|
|
||||||
|
|
||||||
从源代码树的根目录运行:
|
|
||||||
|
|
||||||
TAGS="bindata" make build
|
|
||||||
|
|
||||||
如果需要 SQLite 支持:
|
|
||||||
|
|
||||||
TAGS="bindata sqlite sqlite_unlock_notify" make build
|
|
||||||
|
|
||||||
`build` 目标分为两个子目标:
|
|
||||||
|
|
||||||
- `make backend` 需要 [Go Stable](https://go.dev/dl/),所需版本在 [go.mod](/go.mod) 中定义。
|
|
||||||
- `make frontend` 需要 [Node.js LTS](https://nodejs.org/en/download/) 或更高版本。
|
|
||||||
|
|
||||||
需要互联网连接来下载 go 和 npm 模块。从包含预构建前端文件的官方源代码压缩包构建时,不会触发 `frontend` 目标,因此可以在没有 Node.js 的情况下构建。
|
|
||||||
|
|
||||||
更多信息:https://docs.gitea.com/installation/install-from-source
|
|
||||||
|
|
||||||
## 使用
|
|
||||||
|
|
||||||
构建后,默认情况下会在源代码树的根目录生成一个名为 `gitea` 的二进制文件。要运行它,请使用:
|
|
||||||
|
|
||||||
./gitea web
|
|
||||||
|
|
||||||
> [!注意]
|
|
||||||
> 如果您对使用我们的 API 感兴趣,我们提供了实验性支持,并附有 [文件](https://docs.gitea.com/api)。
|
|
||||||
|
|
||||||
## 贡献
|
|
||||||
|
|
||||||
预期的工作流程是:Fork -> Patch -> Push -> Pull Request
|
|
||||||
|
|
||||||
> [!注意]
|
|
||||||
>
|
|
||||||
> 1. **在开始进行 Pull Request 之前,您必须阅读 [贡献者指南](CONTRIBUTING.md)。**
|
|
||||||
> 2. 如果您在项目中发现了漏洞,请私下写信给 **security@gitea.io**。谢谢!
|
|
||||||
|
|
||||||
## 翻译
|
|
||||||
|
|
||||||
[](https://translate.gitea.com)
|
|
||||||
|
|
||||||
翻译通过 [Crowdin](https://translate.gitea.com) 进行。如果您想翻译成新的语言,请在 Crowdin 项目中请求管理员添加新语言。
|
|
||||||
|
|
||||||
您也可以创建一个 issue 来添加语言,或者在 discord 的 #translation 频道上询问。如果您需要上下文或发现一些翻译问题,可以在字符串上留言或在 Discord 上询问。对于一般的翻译问题,文档中有一个部分。目前有点空,但我们希望随着问题的出现而填充它。
|
|
||||||
|
|
||||||
更多信息请参阅 [文件](https://docs.gitea.com/contributing/localization)。
|
|
||||||
|
|
||||||
## 官方和第三方项目
|
|
||||||
|
|
||||||
我们提供了一个官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一个名为 [tea](https://gitea.com/gitea/tea) 的 CLI 工具和一个 Gitea Action 的 [action runner](https://gitea.com/gitea/act_runner)。
|
|
||||||
|
|
||||||
我们在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 维护了一个 Gitea 相关项目的列表,您可以在那里发现更多的第三方项目,包括 SDK、插件、主题等。
|
|
||||||
|
|
||||||
## 通讯
|
|
||||||
|
|
||||||
[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
|
|
||||||
|
|
||||||
如果您有任何文件未涵盖的问题,可以在我们的 [Discord 服务器](https://discord.gg/Gitea) 上与我们联系,或者在 [discourse 论坛](https://forum.gitea.com/) 上创建帖子。
|
|
||||||
|
|
||||||
## 作者
|
|
||||||
|
|
||||||
- [维护者](https://github.com/orgs/go-gitea/people)
|
|
||||||
- [贡献者](https://github.com/go-gitea/gitea/graphs/contributors)
|
|
||||||
- [翻译者](options/locale/TRANSLATORS)
|
|
||||||
|
|
||||||
## 支持者
|
|
||||||
|
|
||||||
感谢所有支持者! 🙏 [[成为支持者](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>
|
|
||||||
|
|
||||||
## 赞助商
|
|
||||||
|
|
||||||
通过成为赞助商来支持这个项目。您的标志将显示在这里,并带有链接到您的网站。 [[成为赞助商](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>
|
|
||||||
|
|
||||||
## 常见问题
|
|
||||||
|
|
||||||
**Gitea 怎么发音?**
|
|
||||||
|
|
||||||
Gitea 的发音是 [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY),就像 "gi-tea" 一样,g 是硬音。
|
|
||||||
|
|
||||||
**为什么这个项目没有托管在 Gitea 实例上?**
|
|
||||||
|
|
||||||
我们正在 [努力](https://github.com/go-gitea/gitea/issues/1029)。
|
|
||||||
|
|
||||||
**在哪里可以找到安全补丁?**
|
|
||||||
|
|
||||||
在 [发布日志](https://github.com/go-gitea/gitea/releases) 或 [变更日志](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) 中,搜索关键词 `SECURITY` 以找到安全补丁。
|
|
||||||
|
|
||||||
## 许可证
|
|
||||||
|
|
||||||
这个项目是根据 MIT 许可证授权的。
|
|
||||||
请参阅 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件以获取完整的许可证文本。
|
|
||||||
|
|
||||||
## 进一步信息
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>寻找界面概述?查看这里!</summary>
|
|
||||||
|
|
||||||
### 登录/注册页面
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
### 用户仪表板
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
### 用户资料
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 探索
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
### 仓库
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
#### 仓库问题
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
#### 仓库拉取请求
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
#### 仓库操作
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
#### 仓库活动
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
### 组织
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</details>
|
|
||||||
206
README.zh-tw.md
206
README.zh-tw.md
@@ -1,206 +0,0 @@
|
|||||||
# Gitea
|
|
||||||
|
|
||||||
[](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
|
|
||||||
[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
|
|
||||||
[](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
|
|
||||||
[](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
|
|
||||||
[](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
|
|
||||||
[](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
|
|
||||||
[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
|
|
||||||
[](https://opensource.org/licenses/MIT "License: MIT")
|
|
||||||
[](https://gitpod.io/#https://github.com/go-gitea/gitea)
|
|
||||||
[](https://translate.gitea.com "Crowdin")
|
|
||||||
|
|
||||||
[English](./README.md) | [简体中文](./README.zh-cn.md)
|
|
||||||
|
|
||||||
## 目的
|
|
||||||
|
|
||||||
這個項目的目標是提供最簡單、最快速、最無痛的方式來設置自託管的 Git 服務。
|
|
||||||
|
|
||||||
由於 Gitea 是用 Go 語言編寫的,它可以在 Go 支援的所有平台和架構上運行,包括 Linux、macOS 和 Windows 的 x86、amd64、ARM 和 PowerPC 架構。這個項目自 2016 年 11 月從 [Gogs](https://gogs.io) [分叉](https://blog.gitea.com/welcome-to-gitea/) 而來,但已經有了很多變化。
|
|
||||||
|
|
||||||
在線演示可以訪問 [demo.gitea.com](https://demo.gitea.com)。
|
|
||||||
|
|
||||||
要訪問免費的 Gitea 服務(有一定數量的倉庫限制),可以訪問 [gitea.com](https://gitea.com/user/login)。
|
|
||||||
|
|
||||||
要快速部署您自己的專用 Gitea 實例,可以在 [cloud.gitea.com](https://cloud.gitea.com) 開始免費試用。
|
|
||||||
|
|
||||||
## 文件
|
|
||||||
|
|
||||||
您可以在我們的官方 [文件網站](https://docs.gitea.com/) 上找到全面的文件。
|
|
||||||
|
|
||||||
它包括安裝、管理、使用、開發、貢獻指南等,幫助您快速入門並有效地探索所有功能。
|
|
||||||
|
|
||||||
如果您有任何建議或想要貢獻,可以訪問 [文件倉庫](https://gitea.com/gitea/docs)
|
|
||||||
|
|
||||||
## 構建
|
|
||||||
|
|
||||||
從源代碼樹的根目錄運行:
|
|
||||||
|
|
||||||
TAGS="bindata" make build
|
|
||||||
|
|
||||||
如果需要 SQLite 支援:
|
|
||||||
|
|
||||||
TAGS="bindata sqlite sqlite_unlock_notify" make build
|
|
||||||
|
|
||||||
`build` 目標分為兩個子目標:
|
|
||||||
|
|
||||||
- `make backend` 需要 [Go Stable](https://go.dev/dl/),所需版本在 [go.mod](/go.mod) 中定義。
|
|
||||||
- `make frontend` 需要 [Node.js LTS](https://nodejs.org/en/download/) 或更高版本。
|
|
||||||
|
|
||||||
需要互聯網連接來下載 go 和 npm 模塊。從包含預構建前端文件的官方源代碼壓縮包構建時,不會觸發 `frontend` 目標,因此可以在沒有 Node.js 的情況下構建。
|
|
||||||
|
|
||||||
更多信息:https://docs.gitea.com/installation/install-from-source
|
|
||||||
|
|
||||||
## 使用
|
|
||||||
|
|
||||||
構建後,默認情況下會在源代碼樹的根目錄生成一個名為 `gitea` 的二進制文件。要運行它,請使用:
|
|
||||||
|
|
||||||
./gitea web
|
|
||||||
|
|
||||||
> [!注意]
|
|
||||||
> 如果您對使用我們的 API 感興趣,我們提供了實驗性支援,並附有 [文件](https://docs.gitea.com/api)。
|
|
||||||
|
|
||||||
## 貢獻
|
|
||||||
|
|
||||||
預期的工作流程是:Fork -> Patch -> Push -> Pull Request
|
|
||||||
|
|
||||||
> [!注意]
|
|
||||||
>
|
|
||||||
> 1. **在開始進行 Pull Request 之前,您必須閱讀 [貢獻者指南](CONTRIBUTING.md)。**
|
|
||||||
> 2. 如果您在項目中發現了漏洞,請私下寫信給 **security@gitea.io**。謝謝!
|
|
||||||
|
|
||||||
## 翻譯
|
|
||||||
|
|
||||||
[](https://translate.gitea.com)
|
|
||||||
|
|
||||||
翻譯通過 [Crowdin](https://translate.gitea.com) 進行。如果您想翻譯成新的語言,請在 Crowdin 項目中請求管理員添加新語言。
|
|
||||||
|
|
||||||
您也可以創建一個 issue 來添加語言,或者在 discord 的 #translation 頻道上詢問。如果您需要上下文或發現一些翻譯問題,可以在字符串上留言或在 Discord 上詢問。對於一般的翻譯問題,文檔中有一個部分。目前有點空,但我們希望隨著問題的出現而填充它。
|
|
||||||
|
|
||||||
更多信息請參閱 [文件](https://docs.gitea.com/contributing/localization)。
|
|
||||||
|
|
||||||
## 官方和第三方項目
|
|
||||||
|
|
||||||
我們提供了一個官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一個名為 [tea](https://gitea.com/gitea/tea) 的 CLI 工具和一個 Gitea Action 的 [action runner](https://gitea.com/gitea/act_runner)。
|
|
||||||
|
|
||||||
我們在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 維護了一個 Gitea 相關項目的列表,您可以在那裡發現更多的第三方項目,包括 SDK、插件、主題等。
|
|
||||||
|
|
||||||
## 通訊
|
|
||||||
|
|
||||||
[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
|
|
||||||
|
|
||||||
如果您有任何文件未涵蓋的問題,可以在我們的 [Discord 服務器](https://discord.gg/Gitea) 上與我們聯繫,或者在 [discourse 論壇](https://forum.gitea.com/) 上創建帖子。
|
|
||||||
|
|
||||||
## 作者
|
|
||||||
|
|
||||||
- [維護者](https://github.com/orgs/go-gitea/people)
|
|
||||||
- [貢獻者](https://github.com/go-gitea/gitea/graphs/contributors)
|
|
||||||
- [翻譯者](options/locale/TRANSLATORS)
|
|
||||||
|
|
||||||
## 支持者
|
|
||||||
|
|
||||||
感謝所有支持者! 🙏 [[成為支持者](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>
|
|
||||||
|
|
||||||
## 贊助商
|
|
||||||
|
|
||||||
通過成為贊助商來支持這個項目。您的標誌將顯示在這裡,並帶有鏈接到您的網站。 [[成為贊助商](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>
|
|
||||||
|
|
||||||
## 常見問題
|
|
||||||
|
|
||||||
**Gitea 怎麼發音?**
|
|
||||||
|
|
||||||
Gitea 的發音是 [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY),就像 "gi-tea" 一樣,g 是硬音。
|
|
||||||
|
|
||||||
**為什麼這個項目沒有託管在 Gitea 實例上?**
|
|
||||||
|
|
||||||
我們正在 [努力](https://github.com/go-gitea/gitea/issues/1029)。
|
|
||||||
|
|
||||||
**在哪裡可以找到安全補丁?**
|
|
||||||
|
|
||||||
在 [發佈日誌](https://github.com/go-gitea/gitea/releases) 或 [變更日誌](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) 中,搜索關鍵詞 `SECURITY` 以找到安全補丁。
|
|
||||||
|
|
||||||
## 許可證
|
|
||||||
|
|
||||||
這個項目是根據 MIT 許可證授權的。
|
|
||||||
請參閱 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件以獲取完整的許可證文本。
|
|
||||||
|
|
||||||
## 進一步信息
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>尋找界面概述?查看這裡!</summary>
|
|
||||||
|
|
||||||
### 登錄/註冊頁面
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
### 用戶儀表板
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
### 用戶資料
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 探索
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
### 倉庫
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
#### 倉庫問題
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
#### 倉庫拉取請求
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
#### 倉庫操作
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
#### 倉庫活動
|
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
### 組織
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</details>
|
|
||||||
98
README_ZH.md
Normal file
98
README_ZH.md
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<p align="center">
|
||||||
|
<a href="https://gitea.io/">
|
||||||
|
<img alt="Gitea" src="https://raw.githubusercontent.com/go-gitea/gitea/main/public/img/gitea.svg" width="220"/>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<h1 align="center">Gitea - Git with a cup of tea</h1>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://drone.gitea.io/go-gitea/gitea" title="Build Status">
|
||||||
|
<img src="https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg?ref=refs/heads/main">
|
||||||
|
</a>
|
||||||
|
<a href="https://discord.gg/Gitea" title="Join the Discord chat at https://discord.gg/Gitea">
|
||||||
|
<img src="https://img.shields.io/discord/322538954119184384.svg">
|
||||||
|
</a>
|
||||||
|
<a href="https://app.codecov.io/gh/go-gitea/gitea" title="Codecov">
|
||||||
|
<img src="https://codecov.io/gh/go-gitea/gitea/branch/main/graph/badge.svg">
|
||||||
|
</a>
|
||||||
|
<a href="https://goreportcard.com/report/code.gitea.io/gitea" title="Go Report Card">
|
||||||
|
<img src="https://goreportcard.com/badge/code.gitea.io/gitea">
|
||||||
|
</a>
|
||||||
|
<a href="https://pkg.go.dev/code.gitea.io/gitea" title="GoDoc">
|
||||||
|
<img src="https://pkg.go.dev/badge/code.gitea.io/gitea?status.svg">
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/go-gitea/gitea/releases/latest" title="GitHub release">
|
||||||
|
<img src="https://img.shields.io/github/release/go-gitea/gitea.svg">
|
||||||
|
</a>
|
||||||
|
<a href="https://www.codetriage.com/go-gitea/gitea" title="Help Contribute to Open Source">
|
||||||
|
<img src="https://www.codetriage.com/go-gitea/gitea/badges/users.svg">
|
||||||
|
</a>
|
||||||
|
<a href="https://opencollective.com/gitea" title="Become a backer/sponsor of gitea">
|
||||||
|
<img src="https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen">
|
||||||
|
</a>
|
||||||
|
<a href="https://opensource.org/licenses/MIT" title="License: MIT">
|
||||||
|
<img src="https://img.shields.io/badge/License-MIT-blue.svg">
|
||||||
|
</a>
|
||||||
|
<a href="https://gitpod.io/#https://github.com/go-gitea/gitea">
|
||||||
|
<img
|
||||||
|
src="https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod"
|
||||||
|
alt="Contribute with Gitpod"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href="https://crowdin.com/project/gitea" title="Crowdin">
|
||||||
|
<img src="https://badges.crowdin.net/gitea/localized.svg">
|
||||||
|
</a>
|
||||||
|
<a href="https://www.tickgit.com/browse?repo=github.com/go-gitea/gitea&branch=main" title="TODOs">
|
||||||
|
<img src="https://badgen.net/https/api.tickgit.com/badgen/github.com/go-gitea/gitea/main">
|
||||||
|
</a>
|
||||||
|
<a href="https://app.bountysource.com/teams/gitea" title="Bountysource">
|
||||||
|
<img src="https://img.shields.io/bountysource/team/gitea/activity">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="README.md">View this document in English</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## 目标
|
||||||
|
|
||||||
|
Gitea 的首要目标是创建一个极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。我们采用 Go 作为后端语言,这使我们只要生成一个可执行程序即可。并且他还支持跨平台,支持 Linux, macOS 和 Windows 以及各种架构,除了 x86,amd64,还包括 ARM 和 PowerPC。
|
||||||
|
|
||||||
|
如果您想试用一下,请访问 [在线Demo](https://try.gitea.io/)!
|
||||||
|
|
||||||
|
## 提示
|
||||||
|
|
||||||
|
1. **开始贡献代码之前请确保你已经看过了 [贡献者向导(英文)](CONTRIBUTING.md)**.
|
||||||
|
2. 所有的安全问题,请私下发送邮件给 **security@gitea.io**。谢谢!
|
||||||
|
3. 如果你要使用API,请参见 [API 文档](https://godoc.org/code.gitea.io/sdk/gitea).
|
||||||
|
|
||||||
|
## 文档
|
||||||
|
|
||||||
|
关于如何安装请访问我们的 [文档站](https://docs.gitea.io/zh-cn/),如果没有找到对应的文档,你也可以通过 [Discord - 英文](https://discord.gg/gitea) 和 QQ群 328432459 来和我们交流。
|
||||||
|
|
||||||
|
## 贡献流程
|
||||||
|
|
||||||
|
Fork -> Patch -> Push -> Pull Request
|
||||||
|
|
||||||
|
## 翻译
|
||||||
|
|
||||||
|
多语言翻译是基于Crowdin进行的.
|
||||||
|
[](https://crowdin.com/project/gitea)
|
||||||
|
|
||||||
|
## 作者
|
||||||
|
|
||||||
|
* [Maintainers](https://github.com/orgs/go-gitea/people)
|
||||||
|
* [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
|
||||||
|
* [Translators](options/locale/TRANSLATORS)
|
||||||
|
|
||||||
|
## 授权许可
|
||||||
|
|
||||||
|
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件中。
|
||||||
|
|
||||||
|
## 截图
|
||||||
|
|
||||||
|
||||
|
||||||
|
|:---:|:---:|:---:|
|
||||||
|
||||
|
||||||
|
|||
|
||||||
|
|||
|
||||||
64
SECURITY.md
64
SECURITY.md
@@ -4,22 +4,20 @@ The Gitea maintainers take security seriously.
|
|||||||
|
|
||||||
If you discover a security issue, please bring it to their attention right away!
|
If you discover a security issue, please bring it to their attention right away!
|
||||||
|
|
||||||
Previous vulnerabilities are listed at https://about.gitea.com/security.
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
Please **DO NOT** file a public issue, instead send your report privately to `security@gitea.io`.
|
Please **DO NOT** file a public issue, instead send your report privately to `security@gitea.io`.
|
||||||
|
|
||||||
## Protecting Security Information
|
## Protecting Security Information
|
||||||
|
|
||||||
Due to the sensitive nature of security information, you can use the below GPG public key to encrypt your mail body.
|
Due to the sensitive nature of security information, you can use below GPG public key encrypt your mail body.
|
||||||
|
|
||||||
The PGP key is valid until July 9, 2025.
|
The PGP key is valid until June 24, 2024.
|
||||||
|
|
||||||
```
|
```
|
||||||
Key ID: 6FCD2D5B
|
Key ID: 6FCD2D5B
|
||||||
Key Type: RSA
|
Key Type: RSA
|
||||||
Expires: 7/9/2025
|
Expires: 6/24/2024
|
||||||
Key Size: 4096/4096
|
Key Size: 4096/4096
|
||||||
Fingerprint: 3DE0 3D1E 144A 7F06 9359 99DC AAFD 2381 6FCD 2D5B
|
Fingerprint: 3DE0 3D1E 144A 7F06 9359 99DC AAFD 2381 6FCD 2D5B
|
||||||
```
|
```
|
||||||
@@ -40,20 +38,20 @@ q+pHZl24JYR0Kf3T/ZiOC0cGd2QJqpJtg5J6S/OqfX9NH6MsCczO8pUC1N/aHH2X
|
|||||||
CTme2nF56izORqDWKoiICteL3GpYsCV9nyCidcCmoQsS+DKvE86YhIhVIVWGRY2F
|
CTme2nF56izORqDWKoiICteL3GpYsCV9nyCidcCmoQsS+DKvE86YhIhVIVWGRY2F
|
||||||
lzpAjnN9/KLtQroutrm+Ft0mdjDiJUeFVl1cOHDhoyfCsQh62HumoyZoZvqzQd6e
|
lzpAjnN9/KLtQroutrm+Ft0mdjDiJUeFVl1cOHDhoyfCsQh62HumoyZoZvqzQd6e
|
||||||
AbN11nq6aViMe2Q3je1AbiBnRnQSHxt1Tc8X4IshO3MQK1Sk7oPI6LA5oQARAQAB
|
AbN11nq6aViMe2Q3je1AbiBnRnQSHxt1Tc8X4IshO3MQK1Sk7oPI6LA5oQARAQAB
|
||||||
tCJHaXRlYSBTZWN1cml0eSA8c2VjdXJpdHlAZ2l0ZWEuaW8+iQJXBBMBCABBAhsD
|
tCJHaXRlYSBTZWN1cml0eSA8c2VjdXJpdHlAZ2l0ZWEuaW8+iQJXBBMBCABBFiEE
|
||||||
BQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAFiEEPeA9HhRKfwaTWZncqv0jgW/N
|
PeA9HhRKfwaTWZncqv0jgW/NLVsFAmK1Z/4CGwMFCQPCZwAFCwkIBwICIgIGFQoJ
|
||||||
LVsFAmaMse0FCQW4fW8ACgkQqv0jgW/NLVtXLg/+PF4G9Jhlui15BTNlEBJAV2P/
|
CAsCBBYCAwECHgcCF4AACgkQqv0jgW/NLVvnyxAAhxyNnWzw/rQO2qhzqicmZM94
|
||||||
1QlAV2krk0fP7tykn0FR9RfGIfVV/kwC1f+ouosYPQDDevl9LWdUIM+g94DtNo2o
|
njSbOg+U2qMBvCdaqCQQeC+uaMmMzkDPanUUmLcyCkWqfCjPNjeSXAkE9npepVJI
|
||||||
7ACpcL3morvt5lVGpIZHL8TbX0qmFRXL/pB/cB+K6IwYvh2mrbp2zH+r4SCRyFYq
|
4HtmgxZQ94OU/h3CLbft+9GVRzUkVI29TSYGdvNtV2/BkNGoFFnKWQr119um0o6A
|
||||||
BjgXYFTI1MylJ1ShAjU6Z+m3oJ+2xs5LzHS0X6zkTjzA2Zl4zQzciQ9T+wJcE7Zi
|
bgha2Uy5uY8o3ZIoiKkiHRaEoWIjjeBxJxYAojsZY4YElUmsQ3ik2joG6rhFesTa
|
||||||
HXdM1+YMF8KGNP8J9Rpug5oNDJ98lgZirRY7c3A/1xmYBiPnULwuuymdqEZO7l70
|
ofVt/bL8G2xzpOG26WGIxBbqf2qjV6OtZ0hu/vtTPHeIWMLq0Mz0V3PEDQWfkGPE
|
||||||
SeAlE1RWYX8kbOBnBb/KY4XwE3Vic1oEzc9DiPWVH1ElX86WNNsFzuyULiwoBoWg
|
i2RYxxYDs2xzJhSQWqTNVLSq0m5xTJnbHhQPfdCX4C2jvFKgLdfmytQq49S7jiJb
|
||||||
pqZGhL9x1p5+46RGQSDczsHM7YGVtfYOiDo2PAVrmwsT0BnXnK8Oe3YIkvmUPEJu
|
Z03HVOZ/PsyBlQfH9xJi06R5yQCMEA8h8Z5r3/NXW09kQ6OFRe6xshoTcxZGRPTo
|
||||||
OkLt0Z6A5n8pz8zhQzuApwBsK4ncJ8zTCpvz/pfKKqZC/Vnoh3gKGhDGvOZ+b5IJ
|
srhwr3uPbmCRh+YEl7qBLU6+BC5k8IRTZXqhrj/aPJu3MxgbgwV8u3vLoFSXM2lb
|
||||||
0kUTe2JsbnwFixDUMDtacQ1op8XOyLoLVmgqLn0+Pws4XPBlMof2bioFir3yHKnP
|
a61FgeCQ0O7lkgVswwF0RppCaH9Ul3ZDapet/vCRg4NVwm9zOI/8q/Vj0FKA1GDR
|
||||||
gNchsF1agrlSIo5GA8u4ga+IlCSfvFIKrl7+cxacKcJYt/vbOU5KcvVJI5EtHKCG
|
JhRu8+Ce8zlFL65D34t+PprAzSeTlbv9um3x/ZIjCco7EEKSBylt+AZj/VyA6+e5
|
||||||
xfHjHY2ah1Qww7SxW6IXiRZZzPpsL2mBM2CD7N3qh9bV2s27wxYCdUodsIZbiyHe
|
kjOQwRRc6dFJWBcorsSI2dG+H+QMF7ZabzmeCcz1v9HjLOPzYHoZAHhCmSppWTvX
|
||||||
oWPzfBnkmiAN8KlZxHm5Ag0EYrVn/gEQALrFLQjCR3GjuHSindz0rd3Fnx/t7Sen
|
AJy6+lhfW2OUTqQeYSi5Ag0EYrVn/gEQALrFLQjCR3GjuHSindz0rd3Fnx/t7Sen
|
||||||
T+p07yCSSoSlmnJHCQmwh4vfg1blyz0zZ4vkIhtpHsEgc+ZAG+WQXSsJ2iRz+eSN
|
T+p07yCSSoSlmnJHCQmwh4vfg1blyz0zZ4vkIhtpHsEgc+ZAG+WQXSsJ2iRz+eSN
|
||||||
GwoOQl4XC3n+QWkc1ws+btr48+6UqXIQU+F8TPQyx/PIgi2nZXJB7f5+mjCqsk46
|
GwoOQl4XC3n+QWkc1ws+btr48+6UqXIQU+F8TPQyx/PIgi2nZXJB7f5+mjCqsk46
|
||||||
XvH4nTr4kJjuqMSR/++wvre2qNQRa/q/dTsK0OaN/mJsdX6Oi+aGNaQJUhIG7F+E
|
XvH4nTr4kJjuqMSR/++wvre2qNQRa/q/dTsK0OaN/mJsdX6Oi+aGNaQJUhIG7F+E
|
||||||
@@ -64,20 +62,20 @@ qoExANj5lUTZPe8M50lI182FrcjAN7dClO3QI6pg7wy0erMxfFly3j8UQ91ysS9T
|
|||||||
s+GsP9I3cmWWQcKYxWHtE8xTXnNCVPFZQj2nwhJzae8ypfOtulBRA3dUKWGKuDH/
|
s+GsP9I3cmWWQcKYxWHtE8xTXnNCVPFZQj2nwhJzae8ypfOtulBRA3dUKWGKuDH/
|
||||||
axFENhUsT397aOU3qkP/od4a64JyNIEo4CTTSPVeWd7njsGqli2U3A4xL2CcyYvt
|
axFENhUsT397aOU3qkP/od4a64JyNIEo4CTTSPVeWd7njsGqli2U3A4xL2CcyYvt
|
||||||
D/MWcMBGEoLSNTswwKdom4FaJpn5KThnK/T0bQcmJblJhoCtppXisbexZnCpuS0x
|
D/MWcMBGEoLSNTswwKdom4FaJpn5KThnK/T0bQcmJblJhoCtppXisbexZnCpuS0x
|
||||||
Zdlm2T14KJ3LABEBAAGJAjwEGAEIACYCGwwWIQQ94D0eFEp/BpNZmdyq/SOBb80t
|
Zdlm2T14KJ3LABEBAAGJAjwEGAEIACYWIQQ94D0eFEp/BpNZmdyq/SOBb80tWwUC
|
||||||
WwUCZoyyjQUJBbh+DwAKCRCq/SOBb80tW18XD/9MXztmf01MT+1kZdBouZ/7Rp/7
|
YrVn/gIbDAUJA8JnAAAKCRCq/SOBb80tWyTBD/9AGpW6QoDF7zYjHAozH9S5RGCA
|
||||||
9kuqo//B1G+RXau4oFtPqb67kNe2WaIc3u5B73PUHsMf3i6z4ib2KbMhZZerLn0O
|
Y7E82dG/0xmFUwPprAG0BKmmgU6TiipyVGmKIXGYYYU92pMnbvXkYQMoa+WJNncN
|
||||||
dRglcuPeNWmsASY3dH/XVG0cT0zvvWegagd12TJEl3Vs+7XNrOw4cwDj9L1+GH9m
|
D3fY52UeXeffTf4cFpStlzi9xgYtOLhFamzYu/4xhkjOX+xhOSXscCiFRyT8cF3B
|
||||||
kSt4uaANWn/6a3RvMRhiVEYuNwhAzcKaactPmYqrLJgoVLbRSDkgyHaMQ2jKgLxk
|
O6c5BHU+Zj0/rGPgOyPUbx7l7B9MubB/41nNX35k08e+8T3wtWDb4XF+15HnRfva
|
||||||
ifS/fvluGV0ub2Po6DJiqfRpd1tDvPhe9y1+r1WFDZsOcvTcZUfSt/7dXMGfqGu0
|
6fblO8wgU25Orv2Rm1jnKGa9DxJ8nE40IMrqDapENtDuL+zKJsvR0+ptWvEyL56U
|
||||||
2daVFlfeSXSALrDE5uc0UxodHCpP3sqRYDZevGLBRaaTkIjYXG/+N898+7K5WJF4
|
GtJJG5un6mXiLKuRQT0DEv4MdZRHDgDstDnqcbEiazVEbUuvhZZob6lRY2A19m1+
|
||||||
xXOLWxM2cwGkG7eC9pugcDnBp9XlF7O+GBiZ05JUe5flXDQFZ+h3exjopu6KHF1B
|
7zfnDxkhqCA1RCnv4fdvcPdCMMFHwLpdhjgW0aI/uwgwrvsEz5+JRlnLvdQHlPAg
|
||||||
RnzNy8LC0UKb+AuvRIOLV92a9Q9wGWU/jaVDu6nZ0umAeuSzxiHoDsonm0Fl9QAz
|
q7l2fGcBSpz9U0ayyfRPjPntsNCtZl1UDxGLeciPkZhyG84zEWQbk/j52ZpRN+Ik
|
||||||
2/xCokebuoeLrEK7R2af3X86mqq3sVO4ax+HPYChzOaVQBiHUW/TAldWcldYYphR
|
ALpRLa8RBFmFSmXDUmwQrmm1EmARyQXwweKU31hf8ZGbCp2lPuRYm1LuGiirXSVP
|
||||||
/e2WsbmQfvCRtz/bZfo+aUVnrHNjzVMtF2SszdVmA/04Y8pS28MqtuRqhm5DPOOd
|
GysjRAJgW+VRpBKOzFQoUAUbReVWSaCwT8s17THzf71DdDb6CTj31jMLLYWwBpA/
|
||||||
g1YeUywK5jRZ1twyo1kzJEFPLaoeaXaycsR1PMVBW0Urik5mrR/pOWq7PPoZoKb2
|
i73DgobDZMIGEZZC1EKqza8eh11xfyHFzGec03tbh+lIen+5IiRtWiEWkDS9ll0G
|
||||||
lXYLE8bwkuQTmsyL1g==
|
zgS/ZdziCvdAutqnGA==
|
||||||
=9i7d
|
=gZWO
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
2
assets/emoji.json
generated
2
assets/emoji.json
generated
File diff suppressed because one or more lines are too long
426
assets/go-licenses.json
generated
426
assets/go-licenses.json
generated
File diff suppressed because one or more lines are too long
@@ -12,7 +12,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/container"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -59,7 +58,7 @@ func main() {
|
|||||||
|
|
||||||
// use old en-US as the base, and copy the new translations to the old locales
|
// use old en-US as the base, and copy the new translations to the old locales
|
||||||
enUsOld := inisOld["options/locale/locale_en-US.ini"]
|
enUsOld := inisOld["options/locale/locale_en-US.ini"]
|
||||||
brokenWarned := make(container.Set[string])
|
brokenWarned := map[string]bool{}
|
||||||
for path, iniOld := range inisOld {
|
for path, iniOld := range inisOld {
|
||||||
if iniOld == enUsOld {
|
if iniOld == enUsOld {
|
||||||
continue
|
continue
|
||||||
@@ -78,7 +77,7 @@ func main() {
|
|||||||
broken := oldStr != "" && strings.Count(oldStr, "%") != strings.Count(newStr, "%")
|
broken := oldStr != "" && strings.Count(oldStr, "%") != strings.Count(newStr, "%")
|
||||||
broken = broken || strings.Contains(oldStr, "\n") || strings.Contains(oldStr, "\n")
|
broken = broken || strings.Contains(oldStr, "\n") || strings.Contains(oldStr, "\n")
|
||||||
if broken {
|
if broken {
|
||||||
brokenWarned.Add(secOld.Name() + "." + keyEnUs.Name())
|
brokenWarned[secOld.Name()+"."+keyEnUs.Name()] = true
|
||||||
fmt.Println("----")
|
fmt.Println("----")
|
||||||
fmt.Printf("WARNING: skip broken locale: %s , [%s] %s\n", path, secEnUS.Name(), keyEnUs.Name())
|
fmt.Printf("WARNING: skip broken locale: %s , [%s] %s\n", path, secEnUS.Name(), keyEnUs.Name())
|
||||||
fmt.Printf("\told: %s\n", strings.ReplaceAll(oldStr, "\n", "\\n"))
|
fmt.Printf("\told: %s\n", strings.ReplaceAll(oldStr, "\n", "\\n"))
|
||||||
@@ -104,7 +103,7 @@ func main() {
|
|||||||
broken = broken || strings.HasPrefix(str, "`\"")
|
broken = broken || strings.HasPrefix(str, "`\"")
|
||||||
broken = broken || strings.Count(str, `"`)%2 == 1
|
broken = broken || strings.Count(str, `"`)%2 == 1
|
||||||
broken = broken || strings.Count(str, "`")%2 == 1
|
broken = broken || strings.Count(str, "`")%2 == 1
|
||||||
if broken && !brokenWarned.Contains(sec.Name()+"."+key.Name()) {
|
if broken && !brokenWarned[sec.Name()+"."+key.Name()] {
|
||||||
fmt.Printf("WARNING: found broken locale: %s , [%s] %s\n", path, sec.Name(), key.Name())
|
fmt.Printf("WARNING: found broken locale: %s , [%s] %s\n", path, sec.Name(), key.Name())
|
||||||
fmt.Printf("\tstr: %s\n", strings.ReplaceAll(str, "\n", "\\n"))
|
fmt.Printf("\tstr: %s\n", strings.ReplaceAll(str, "\n", "\\n"))
|
||||||
fmt.Println("----")
|
fmt.Println("----")
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ func newFileCollector(fileFilter string, batchSize int) (*fileCollector, error)
|
|||||||
co.includePatterns = append(co.includePatterns, regexp.MustCompile(`.*\.go$`))
|
co.includePatterns = append(co.includePatterns, regexp.MustCompile(`.*\.go$`))
|
||||||
|
|
||||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`.*\bbindata\.go$`))
|
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`.*\bbindata\.go$`))
|
||||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`\.pb\.go$`))
|
|
||||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/gitea-repositories-meta`))
|
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/gitea-repositories-meta`))
|
||||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/integration/migration-test`))
|
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/integration/migration-test`))
|
||||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`modules/git/tests`))
|
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`modules/git/tests`))
|
||||||
@@ -204,6 +203,17 @@ Example:
|
|||||||
`, "file-batch-exec")
|
`, "file-batch-exec")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getGoVersion() string {
|
||||||
|
goModFile, err := os.ReadFile("go.mod")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf(`Faild to read "go.mod": %v`, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
goModVersionRegex := regexp.MustCompile(`go \d+\.\d+`)
|
||||||
|
goModVersionLine := goModVersionRegex.Find(goModFile)
|
||||||
|
return string(goModVersionLine[3:])
|
||||||
|
}
|
||||||
|
|
||||||
func newFileCollectorFromMainOptions(mainOptions map[string]string) (fc *fileCollector, err error) {
|
func newFileCollectorFromMainOptions(mainOptions map[string]string) (fc *fileCollector, err error) {
|
||||||
fileFilter := mainOptions["file-filter"]
|
fileFilter := mainOptions["file-filter"]
|
||||||
if fileFilter == "" {
|
if fileFilter == "" {
|
||||||
@@ -268,8 +278,7 @@ func main() {
|
|||||||
log.Print("the -d option is not supported by gitea-fmt")
|
log.Print("the -d option is not supported by gitea-fmt")
|
||||||
}
|
}
|
||||||
cmdErrors = append(cmdErrors, giteaFormatGoImports(files, containsString(subArgs, "-w")))
|
cmdErrors = append(cmdErrors, giteaFormatGoImports(files, containsString(subArgs, "-w")))
|
||||||
cmdErrors = append(cmdErrors, passThroughCmd("gofmt", append([]string{"-w", "-r", "interface{} -> any"}, substArgs...)))
|
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra", "-lang", getGoVersion()}, substArgs...)))
|
||||||
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra"}, substArgs...)))
|
|
||||||
default:
|
default:
|
||||||
log.Fatalf("unknown cmd: %s %v", subCmd, subArgs)
|
log.Fatalf("unknown cmd: %s %v", subCmd, subArgs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
gemojiURL = "https://raw.githubusercontent.com/github/gemoji/master/db/emoji.json"
|
gemojiURL = "https://raw.githubusercontent.com/github/gemoji/master/db/emoji.json"
|
||||||
maxUnicodeVersion = 15
|
maxUnicodeVersion = 14
|
||||||
)
|
)
|
||||||
|
|
||||||
var flagOut = flag.String("o", "modules/emoji/emoji_data.go", "out")
|
var flagOut = flag.String("o", "modules/emoji/emoji_data.go", "out")
|
||||||
@@ -53,6 +53,8 @@ func (e Emoji) MarshalJSON() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
var err error
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
// generate data
|
// generate data
|
||||||
@@ -81,6 +83,8 @@ var replacer = strings.NewReplacer(
|
|||||||
var emojiRE = regexp.MustCompile(`\{Emoji:"([^"]*)"`)
|
var emojiRE = regexp.MustCompile(`\{Emoji:"([^"]*)"`)
|
||||||
|
|
||||||
func generate() ([]byte, error) {
|
func generate() ([]byte, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
// load gemoji data
|
// load gemoji data
|
||||||
res, err := http.Get(gemojiURL)
|
res, err := http.Get(gemojiURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/container"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// regexp is based on go-license, excluding README and NOTICE
|
// regexp is based on go-license, excluding README and NOTICE
|
||||||
@@ -57,14 +55,20 @@ func main() {
|
|||||||
// yml
|
// yml
|
||||||
//
|
//
|
||||||
// It could be removed once we have a better regex.
|
// It could be removed once we have a better regex.
|
||||||
excludedExt := container.SetOf(".gitignore", ".go", ".mod", ".sum", ".toml", ".yml")
|
excludedExt := map[string]bool{
|
||||||
|
".gitignore": true,
|
||||||
|
".go": true,
|
||||||
|
".mod": true,
|
||||||
|
".sum": true,
|
||||||
|
".toml": true,
|
||||||
|
".yml": true,
|
||||||
|
}
|
||||||
var paths []string
|
var paths []string
|
||||||
err := filepath.WalkDir(base, func(path string, entry fs.DirEntry, err error) error {
|
err := filepath.WalkDir(base, func(path string, entry fs.DirEntry, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if entry.IsDir() || !licenseRe.MatchString(entry.Name()) || excludedExt.Contains(filepath.Ext(entry.Name())) {
|
if entry.IsDir() || !licenseRe.MatchString(entry.Name()) || excludedExt[filepath.Ext(entry.Name())] {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
paths = append(paths, path)
|
paths = append(paths, path)
|
||||||
|
|||||||
82
build/generate-images.js
Executable file
82
build/generate-images.js
Executable file
@@ -0,0 +1,82 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import imageminZopfli from 'imagemin-zopfli';
|
||||||
|
import {optimize} from 'svgo';
|
||||||
|
import {fabric} from 'fabric';
|
||||||
|
import {readFile, writeFile} from 'node:fs/promises';
|
||||||
|
|
||||||
|
function exit(err) {
|
||||||
|
if (err) console.error(err);
|
||||||
|
process.exit(err ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSvg(svg) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
fabric.loadSVGFromString(svg, (objects, options) => {
|
||||||
|
resolve({objects, options});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generate(svg, path, {size, bg}) {
|
||||||
|
const outputFile = new URL(path, import.meta.url);
|
||||||
|
|
||||||
|
if (String(outputFile).endsWith('.svg')) {
|
||||||
|
const {data} = optimize(svg, {
|
||||||
|
plugins: [
|
||||||
|
'preset-default',
|
||||||
|
'removeDimensions',
|
||||||
|
{
|
||||||
|
name: 'addAttributesToSVGElement',
|
||||||
|
params: {attributes: [{width: size}, {height: size}]}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await writeFile(outputFile, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {objects, options} = await loadSvg(svg);
|
||||||
|
const canvas = new fabric.Canvas();
|
||||||
|
canvas.setDimensions({width: size, height: size});
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.scale(options.width ? (size / options.width) : 1, options.height ? (size / options.height) : 1);
|
||||||
|
|
||||||
|
if (bg) {
|
||||||
|
canvas.add(new fabric.Rect({
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
height: size * (1 / (size / options.height)),
|
||||||
|
width: size * (1 / (size / options.width)),
|
||||||
|
fill: 'white',
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.add(fabric.util.groupSVGElements(objects, options));
|
||||||
|
canvas.renderAll();
|
||||||
|
|
||||||
|
let png = Buffer.from([]);
|
||||||
|
for await (const chunk of canvas.createPNGStream()) {
|
||||||
|
png = Buffer.concat([png, chunk]);
|
||||||
|
}
|
||||||
|
|
||||||
|
png = await imageminZopfli({more: true})(png);
|
||||||
|
await writeFile(outputFile, png);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const gitea = process.argv.slice(2).includes('gitea');
|
||||||
|
const logoSvg = await readFile(new URL('../assets/logo.svg', import.meta.url), 'utf8');
|
||||||
|
const faviconSvg = await readFile(new URL('../assets/favicon.svg', import.meta.url), 'utf8');
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
generate(logoSvg, '../public/img/logo.svg', {size: 32}),
|
||||||
|
generate(logoSvg, '../public/img/logo.png', {size: 512}),
|
||||||
|
generate(faviconSvg, '../public/img/favicon.svg', {size: 32}),
|
||||||
|
generate(faviconSvg, '../public/img/favicon.png', {size: 180}),
|
||||||
|
generate(logoSvg, '../public/img/avatar_default.png', {size: 200}),
|
||||||
|
generate(logoSvg, '../public/img/apple-touch-icon.png', {size: 180, bg: true}),
|
||||||
|
gitea && generate(logoSvg, '../public/img/gitea.svg', {size: 32}),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().then(exit).catch(exit);
|
||||||
122
build/generate-licenses.go
Normal file
122
build/generate-licenses.go
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
//go:build ignore
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"compress/gzip"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
prefix = "gitea-licenses"
|
||||||
|
url = "https://api.github.com/repos/spdx/license-list-data/tarball"
|
||||||
|
githubApiToken = ""
|
||||||
|
githubUsername = ""
|
||||||
|
destination = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
flag.StringVar(&destination, "dest", "options/license/", "destination for the licenses")
|
||||||
|
flag.StringVar(&githubUsername, "username", "", "github username")
|
||||||
|
flag.StringVar(&githubApiToken, "token", "", "github api token")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
file, err := os.CreateTemp(os.TempDir(), prefix)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create temp file. %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer util.Remove(file.Name())
|
||||||
|
|
||||||
|
if err := os.RemoveAll(destination); err != nil {
|
||||||
|
log.Fatalf("Cannot clean destination folder: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(destination, 0o755); err != nil {
|
||||||
|
log.Fatalf("Cannot create destination: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to download archive. %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(githubApiToken) > 0 && len(githubUsername) > 0 {
|
||||||
|
req.SetBasicAuth(githubUsername, githubApiToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to download archive. %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if _, err := io.Copy(file, resp.Body); err != nil {
|
||||||
|
log.Fatalf("Failed to copy archive to file. %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := file.Seek(0, 0); err != nil {
|
||||||
|
log.Fatalf("Failed to reset seek on archive. %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gz, err := gzip.NewReader(file)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to gunzip the archive. %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tr := tar.NewReader(gz)
|
||||||
|
|
||||||
|
for {
|
||||||
|
hdr, err := tr.Next()
|
||||||
|
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to iterate archive. %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(hdr.Name, "/text/") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if filepath.Ext(hdr.Name) != ".txt" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(filepath.Base(hdr.Name), "README") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(filepath.Base(hdr.Name), "deprecated_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out, err := os.Create(path.Join(destination, strings.TrimSuffix(filepath.Base(hdr.Name), ".txt")))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create new file. %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
if _, err := io.Copy(out, tr); err != nil {
|
||||||
|
log.Fatalf("Failed to write new file. %s", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Written %s\n", out.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Done")
|
||||||
|
}
|
||||||
66
build/generate-svg.js
Executable file
66
build/generate-svg.js
Executable file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import fastGlob from 'fast-glob';
|
||||||
|
import {optimize} from 'svgo';
|
||||||
|
import {parse} from 'node:path';
|
||||||
|
import {readFile, writeFile, mkdir} from 'node:fs/promises';
|
||||||
|
import {fileURLToPath} from 'node:url';
|
||||||
|
|
||||||
|
const glob = (pattern) => fastGlob.sync(pattern, {
|
||||||
|
cwd: fileURLToPath(new URL('..', import.meta.url)),
|
||||||
|
absolute: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
function exit(err) {
|
||||||
|
if (err) console.error(err);
|
||||||
|
process.exit(err ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function processFile(file, {prefix, fullName} = {}) {
|
||||||
|
let name;
|
||||||
|
if (fullName) {
|
||||||
|
name = fullName;
|
||||||
|
} else {
|
||||||
|
name = parse(file).name;
|
||||||
|
if (prefix) name = `${prefix}-${name}`;
|
||||||
|
if (prefix === 'octicon') name = name.replace(/-[0-9]+$/, ''); // chop of '-16' on octicons
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the `xmlns` attribute so that the files are displayable in standalone documents
|
||||||
|
// The svg backend module will strip the attribute during startup for inline display
|
||||||
|
const {data} = optimize(await readFile(file, 'utf8'), {
|
||||||
|
plugins: [
|
||||||
|
{name: 'preset-default'},
|
||||||
|
{name: 'removeDimensions'},
|
||||||
|
{name: 'prefixIds', params: {prefix: () => name}},
|
||||||
|
{name: 'addClassesToSVGElement', params: {classNames: ['svg', name]}},
|
||||||
|
{
|
||||||
|
name: 'addAttributesToSVGElement', params: {
|
||||||
|
attributes: [
|
||||||
|
{'xmlns': 'http://www.w3.org/2000/svg'},
|
||||||
|
{'width': '16'}, {'height': '16'}, {'aria-hidden': 'true'},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await writeFile(fileURLToPath(new URL(`../public/img/svg/${name}.svg`, import.meta.url)), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function processFiles(pattern, opts) {
|
||||||
|
return glob(pattern).map((file) => processFile(file, opts));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
await mkdir(fileURLToPath(new URL('../public/img/svg', import.meta.url)), {recursive: true});
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
...processFiles('node_modules/@primer/octicons/build/svg/*-16.svg', {prefix: 'octicon'}),
|
||||||
|
...processFiles('web_src/svg/*.svg'),
|
||||||
|
...processFiles('public/img/gitea.svg', {fullName: 'gitea-gitea'}),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().then(exit).catch(exit);
|
||||||
@@ -9,30 +9,30 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// CmdActions represents the available actions sub-commands.
|
// CmdActions represents the available actions sub-commands.
|
||||||
CmdActions = &cli.Command{
|
CmdActions = cli.Command{
|
||||||
Name: "actions",
|
Name: "actions",
|
||||||
Usage: "Manage Gitea Actions",
|
Usage: "",
|
||||||
Subcommands: []*cli.Command{
|
Description: "Commands for managing Gitea Actions",
|
||||||
|
Subcommands: []cli.Command{
|
||||||
subcmdActionsGenRunnerToken,
|
subcmdActionsGenRunnerToken,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
subcmdActionsGenRunnerToken = &cli.Command{
|
subcmdActionsGenRunnerToken = cli.Command{
|
||||||
Name: "generate-runner-token",
|
Name: "generate-runner-token",
|
||||||
Usage: "Generate a new token for a runner to use to register with the server",
|
Usage: "Generate a new token for a runner to use to register with the server",
|
||||||
Action: runGenerateActionsRunnerToken,
|
Action: runGenerateActionsRunnerToken,
|
||||||
Aliases: []string{"grt"},
|
Aliases: []string{"grt"},
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "scope",
|
Name: "scope, s",
|
||||||
Aliases: []string{"s"},
|
Value: "",
|
||||||
Value: "",
|
Usage: "{owner}[/{repo}] - leave empty for a global runner",
|
||||||
Usage: "{owner}[/{repo}] - leave empty for a global runner",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -50,6 +50,6 @@ func runGenerateActionsRunnerToken(c *cli.Context) error {
|
|||||||
if extra.HasError() {
|
if extra.HasError() {
|
||||||
return handleCliResponseExtra(extra)
|
return handleCliResponseExtra(extra)
|
||||||
}
|
}
|
||||||
_, _ = fmt.Printf("%s\n", respText.Text)
|
_, _ = fmt.Printf("%s\n", respText)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
679
cmd/admin.go
679
cmd/admin.go
@@ -5,25 +5,36 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"text/tabwriter"
|
||||||
|
|
||||||
|
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||||
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/gitrepo"
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
auth_service "code.gitea.io/gitea/services/auth"
|
||||||
|
"code.gitea.io/gitea/services/auth/source/oauth2"
|
||||||
|
"code.gitea.io/gitea/services/auth/source/smtp"
|
||||||
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// CmdAdmin represents the available admin sub-command.
|
// CmdAdmin represents the available admin sub-command.
|
||||||
CmdAdmin = &cli.Command{
|
CmdAdmin = cli.Command{
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
Usage: "Perform common administrative operations",
|
Usage: "Command line interface to perform common administrative operations",
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []cli.Command{
|
||||||
subcmdUser,
|
subcmdUser,
|
||||||
subcmdRepoSyncReleases,
|
subcmdRepoSyncReleases,
|
||||||
subcmdRegenerate,
|
subcmdRegenerate,
|
||||||
@@ -32,31 +43,43 @@ var (
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
subcmdRepoSyncReleases = &cli.Command{
|
subcmdRepoSyncReleases = cli.Command{
|
||||||
Name: "repo-sync-releases",
|
Name: "repo-sync-releases",
|
||||||
Usage: "Synchronize repository releases with tags",
|
Usage: "Synchronize repository releases with tags",
|
||||||
Action: runRepoSyncReleases,
|
Action: runRepoSyncReleases,
|
||||||
}
|
}
|
||||||
|
|
||||||
subcmdRegenerate = &cli.Command{
|
subcmdRegenerate = cli.Command{
|
||||||
Name: "regenerate",
|
Name: "regenerate",
|
||||||
Usage: "Regenerate specific files",
|
Usage: "Regenerate specific files",
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []cli.Command{
|
||||||
microcmdRegenHooks,
|
microcmdRegenHooks,
|
||||||
microcmdRegenKeys,
|
microcmdRegenKeys,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
subcmdAuth = &cli.Command{
|
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",
|
Name: "auth",
|
||||||
Usage: "Modify external auth providers",
|
Usage: "Modify external auth providers",
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []cli.Command{
|
||||||
microcmdAuthAddOauth,
|
microcmdAuthAddOauth,
|
||||||
microcmdAuthUpdateOauth,
|
microcmdAuthUpdateOauth,
|
||||||
microcmdAuthAddLdapBindDn,
|
cmdAuthAddLdapBindDn,
|
||||||
microcmdAuthUpdateLdapBindDn,
|
cmdAuthUpdateLdapBindDn,
|
||||||
microcmdAuthAddLdapSimpleAuth,
|
cmdAuthAddLdapSimpleAuth,
|
||||||
microcmdAuthUpdateLdapSimpleAuth,
|
cmdAuthUpdateLdapSimpleAuth,
|
||||||
microcmdAuthAddSMTP,
|
microcmdAuthAddSMTP,
|
||||||
microcmdAuthUpdateSMTP,
|
microcmdAuthUpdateSMTP,
|
||||||
microcmdAuthList,
|
microcmdAuthList,
|
||||||
@@ -64,33 +87,257 @@ var (
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
subcmdSendMail = &cli.Command{
|
microcmdAuthList = cli.Command{
|
||||||
Name: "sendmail",
|
Name: "list",
|
||||||
Usage: "Send a message to all users",
|
Usage: "List auth sources",
|
||||||
Action: runSendMail,
|
Action: runListAuth,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
cli.IntFlag{
|
||||||
Name: "title",
|
Name: "min-width",
|
||||||
Usage: `a title of a message`,
|
Usage: "Minimal cell width including any padding for the formatted table",
|
||||||
Value: "",
|
Value: 0,
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.IntFlag{
|
||||||
Name: "content",
|
Name: "tab-width",
|
||||||
Usage: "a content of a message",
|
Usage: "width of tab characters in formatted table (equivalent number of spaces)",
|
||||||
Value: "",
|
Value: 8,
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.IntFlag{
|
||||||
Name: "force",
|
Name: "padding",
|
||||||
Aliases: []string{"f"},
|
Usage: "padding added to a cell before computing its width",
|
||||||
Usage: "A flag to bypass a confirmation step",
|
Value: 1,
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "pad-char",
|
||||||
|
Usage: `ASCII char used for padding if padchar == '\\t', the Writer will assume that the width of a '\\t' in the formatted output is tabwidth, and cells are left-aligned independent of align_left (for correct-looking results, tabwidth must correspond to the tab width in the viewer displaying the result)`,
|
||||||
|
Value: "\t",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "vertical-bars",
|
||||||
|
Usage: "Set to true to print vertical bars between columns",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
idFlag = &cli.Int64Flag{
|
idFlag = cli.Int64Flag{
|
||||||
Name: "id",
|
Name: "id",
|
||||||
Usage: "ID of authentication source",
|
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-tenant-id",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Use custom Tenant ID for 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)",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "icon-url",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Custom icon URL for OAuth2 login source",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "skip-local-2fa",
|
||||||
|
Usage: "Set to true to skip local 2fa for users authenticated by this source",
|
||||||
|
},
|
||||||
|
cli.StringSliceFlag{
|
||||||
|
Name: "scopes",
|
||||||
|
Value: nil,
|
||||||
|
Usage: "Scopes to request when to authenticate against this OAuth2 source",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "required-claim-name",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Claim name that has to be set to allow users to login with this source",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "required-claim-value",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Claim value that has to be set to allow users to login with this source",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "group-claim-name",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Claim name providing group names for this source",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "admin-group",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Group Claim value for administrator users",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "restricted-group",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Group Claim value for restricted users",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "group-team-map",
|
||||||
|
Value: "",
|
||||||
|
Usage: "JSON mapping between groups and org teams",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "group-team-map-removal",
|
||||||
|
Usage: "Activate automatic team membership removal depending on groups",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
subcmdSendMail = cli.Command{
|
||||||
|
Name: "sendmail",
|
||||||
|
Usage: "Send a message to all users",
|
||||||
|
Action: runSendMail,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "title",
|
||||||
|
Usage: `a title of a message`,
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "content",
|
||||||
|
Usage: "a content of a message",
|
||||||
|
Value: "",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "force,f",
|
||||||
|
Usage: "A flag to bypass a confirmation step",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
smtpCLIFlags = []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "name",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Application Name",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "auth-type",
|
||||||
|
Value: "PLAIN",
|
||||||
|
Usage: "SMTP Authentication Type (PLAIN/LOGIN/CRAM-MD5) default PLAIN",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "host",
|
||||||
|
Value: "",
|
||||||
|
Usage: "SMTP Host",
|
||||||
|
},
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "port",
|
||||||
|
Usage: "SMTP Port",
|
||||||
|
},
|
||||||
|
cli.BoolTFlag{
|
||||||
|
Name: "force-smtps",
|
||||||
|
Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.",
|
||||||
|
},
|
||||||
|
cli.BoolTFlag{
|
||||||
|
Name: "skip-verify",
|
||||||
|
Usage: "Skip TLS verify.",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "helo-hostname",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Hostname sent with HELO. Leave blank to send current hostname",
|
||||||
|
},
|
||||||
|
cli.BoolTFlag{
|
||||||
|
Name: "disable-helo",
|
||||||
|
Usage: "Disable SMTP helo.",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "allowed-domains",
|
||||||
|
Value: "",
|
||||||
|
Usage: "Leave empty to allow all domains. Separate multiple domains with a comma (',')",
|
||||||
|
},
|
||||||
|
cli.BoolTFlag{
|
||||||
|
Name: "skip-local-2fa",
|
||||||
|
Usage: "Skip 2FA to log on.",
|
||||||
|
},
|
||||||
|
cli.BoolTFlag{
|
||||||
|
Name: "active",
|
||||||
|
Usage: "This Authentication Source is Activated.",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
microcmdAuthAddSMTP = cli.Command{
|
||||||
|
Name: "add-smtp",
|
||||||
|
Usage: "Add new SMTP authentication source",
|
||||||
|
Action: runAddSMTP,
|
||||||
|
Flags: smtpCLIFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
microcmdAuthUpdateSMTP = cli.Command{
|
||||||
|
Name: "update-smtp",
|
||||||
|
Usage: "Update existing SMTP authentication source",
|
||||||
|
Action: runUpdateSMTP,
|
||||||
|
Flags: append(smtpCLIFlags[:1], append([]cli.Flag{idFlag}, smtpCLIFlags[1:]...)...),
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func runRepoSyncReleases(_ *cli.Context) error {
|
func runRepoSyncReleases(_ *cli.Context) error {
|
||||||
@@ -101,10 +348,6 @@ func runRepoSyncReleases(_ *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := git.InitSimple(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Trace("Synchronizing repository releases (this may take a while)")
|
log.Trace("Synchronizing repository releases (this may take a while)")
|
||||||
for page := 1; ; page++ {
|
for page := 1; ; page++ {
|
||||||
repos, count, err := repo_model.SearchRepositoryByName(ctx, &repo_model.SearchRepoOptions{
|
repos, count, err := repo_model.SearchRepositoryByName(ctx, &repo_model.SearchRepoOptions{
|
||||||
@@ -123,25 +366,25 @@ func runRepoSyncReleases(_ *cli.Context) error {
|
|||||||
log.Trace("Processing next %d repos of %d", len(repos), count)
|
log.Trace("Processing next %d repos of %d", len(repos), count)
|
||||||
for _, repo := range repos {
|
for _, repo := range repos {
|
||||||
log.Trace("Synchronizing repo %s with path %s", repo.FullName(), repo.RepoPath())
|
log.Trace("Synchronizing repo %s with path %s", repo.FullName(), repo.RepoPath())
|
||||||
gitRepo, err := gitrepo.OpenRepository(ctx, repo)
|
gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("OpenRepository: %v", err)
|
log.Warn("OpenRepository: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
oldnum, err := getReleaseCount(ctx, repo.ID)
|
oldnum, err := getReleaseCount(repo.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn(" GetReleaseCountByRepoID: %v", err)
|
log.Warn(" GetReleaseCountByRepoID: %v", err)
|
||||||
}
|
}
|
||||||
log.Trace(" currentNumReleases is %d, running SyncReleasesWithTags", oldnum)
|
log.Trace(" currentNumReleases is %d, running SyncReleasesWithTags", oldnum)
|
||||||
|
|
||||||
if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil {
|
if err = repo_module.SyncReleasesWithTags(repo, gitRepo); err != nil {
|
||||||
log.Warn(" SyncReleasesWithTags: %v", err)
|
log.Warn(" SyncReleasesWithTags: %v", err)
|
||||||
gitRepo.Close()
|
gitRepo.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
count, err = getReleaseCount(ctx, repo.ID)
|
count, err = getReleaseCount(repo.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn(" GetReleaseCountByRepoID: %v", err)
|
log.Warn(" GetReleaseCountByRepoID: %v", err)
|
||||||
gitRepo.Close()
|
gitRepo.Close()
|
||||||
@@ -157,12 +400,360 @@ func runRepoSyncReleases(_ *cli.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getReleaseCount(ctx context.Context, id int64) (int64, error) {
|
func getReleaseCount(id int64) (int64, error) {
|
||||||
return db.Count[repo_model.Release](
|
return repo_model.GetReleaseCountByRepoID(
|
||||||
ctx,
|
db.DefaultContext,
|
||||||
|
id,
|
||||||
repo_model.FindReleasesOptions{
|
repo_model.FindReleasesOptions{
|
||||||
RepoID: id,
|
|
||||||
IncludeTags: true,
|
IncludeTags: true,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runRegenerateHooks(_ *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRegenerateKeys(_ *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return asymkey_model.RewriteAllPublicKeys()
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseOAuth2Config(c *cli.Context) *oauth2.Source {
|
||||||
|
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"),
|
||||||
|
Tenant: c.String("custom-tenant-id"),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
customURLMapping = nil
|
||||||
|
}
|
||||||
|
return &oauth2.Source{
|
||||||
|
Provider: c.String("provider"),
|
||||||
|
ClientID: c.String("key"),
|
||||||
|
ClientSecret: c.String("secret"),
|
||||||
|
OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
|
||||||
|
CustomURLMapping: customURLMapping,
|
||||||
|
IconURL: c.String("icon-url"),
|
||||||
|
SkipLocalTwoFA: c.Bool("skip-local-2fa"),
|
||||||
|
Scopes: c.StringSlice("scopes"),
|
||||||
|
RequiredClaimName: c.String("required-claim-name"),
|
||||||
|
RequiredClaimValue: c.String("required-claim-value"),
|
||||||
|
GroupClaimName: c.String("group-claim-name"),
|
||||||
|
AdminGroup: c.String("admin-group"),
|
||||||
|
RestrictedGroup: c.String("restricted-group"),
|
||||||
|
GroupTeamMap: c.String("group-team-map"),
|
||||||
|
GroupTeamMapRemoval: c.Bool("group-team-map-removal"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAddOauth(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
config := parseOAuth2Config(c)
|
||||||
|
if config.Provider == "openidConnect" {
|
||||||
|
discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL)
|
||||||
|
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
|
||||||
|
return fmt.Errorf("invalid Auto Discovery URL: %s (this must be a valid URL starting with http:// or https://)", config.OpenIDConnectAutoDiscoveryURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return auth_model.CreateSource(&auth_model.Source{
|
||||||
|
Type: auth_model.OAuth2,
|
||||||
|
Name: c.String("name"),
|
||||||
|
IsActive: true,
|
||||||
|
Cfg: config,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func runUpdateOauth(c *cli.Context) error {
|
||||||
|
if !c.IsSet("id") {
|
||||||
|
return fmt.Errorf("--id flag is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
source, err := auth_model.GetSourceByID(c.Int64("id"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
oAuth2Config := source.Cfg.(*oauth2.Source)
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.IsSet("icon-url") {
|
||||||
|
oAuth2Config.IconURL = c.String("icon-url")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.IsSet("scopes") {
|
||||||
|
oAuth2Config.Scopes = c.StringSlice("scopes")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.IsSet("required-claim-name") {
|
||||||
|
oAuth2Config.RequiredClaimName = c.String("required-claim-name")
|
||||||
|
}
|
||||||
|
if c.IsSet("required-claim-value") {
|
||||||
|
oAuth2Config.RequiredClaimValue = c.String("required-claim-value")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.IsSet("group-claim-name") {
|
||||||
|
oAuth2Config.GroupClaimName = c.String("group-claim-name")
|
||||||
|
}
|
||||||
|
if c.IsSet("admin-group") {
|
||||||
|
oAuth2Config.AdminGroup = c.String("admin-group")
|
||||||
|
}
|
||||||
|
if c.IsSet("restricted-group") {
|
||||||
|
oAuth2Config.RestrictedGroup = c.String("restricted-group")
|
||||||
|
}
|
||||||
|
if c.IsSet("group-team-map") {
|
||||||
|
oAuth2Config.GroupTeamMap = c.String("group-team-map")
|
||||||
|
}
|
||||||
|
if c.IsSet("group-team-map-removal") {
|
||||||
|
oAuth2Config.GroupTeamMapRemoval = c.Bool("group-team-map-removal")
|
||||||
|
}
|
||||||
|
|
||||||
|
// update custom URL mapping
|
||||||
|
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
|
||||||
|
customURLMapping.Tenant = oAuth2Config.CustomURLMapping.Tenant
|
||||||
|
}
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.IsSet("use-custom-urls") && c.IsSet("custom-tenant-id") {
|
||||||
|
customURLMapping.Tenant = c.String("custom-tenant-id")
|
||||||
|
}
|
||||||
|
|
||||||
|
oAuth2Config.CustomURLMapping = customURLMapping
|
||||||
|
source.Cfg = oAuth2Config
|
||||||
|
|
||||||
|
return auth_model.UpdateSource(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
|
||||||
|
if c.IsSet("auth-type") {
|
||||||
|
conf.Auth = c.String("auth-type")
|
||||||
|
validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"}
|
||||||
|
if !util.SliceContainsString(validAuthTypes, strings.ToUpper(c.String("auth-type"))) {
|
||||||
|
return errors.New("Auth must be one of PLAIN/LOGIN/CRAM-MD5")
|
||||||
|
}
|
||||||
|
conf.Auth = c.String("auth-type")
|
||||||
|
}
|
||||||
|
if c.IsSet("host") {
|
||||||
|
conf.Host = c.String("host")
|
||||||
|
}
|
||||||
|
if c.IsSet("port") {
|
||||||
|
conf.Port = c.Int("port")
|
||||||
|
}
|
||||||
|
if c.IsSet("allowed-domains") {
|
||||||
|
conf.AllowedDomains = c.String("allowed-domains")
|
||||||
|
}
|
||||||
|
if c.IsSet("force-smtps") {
|
||||||
|
conf.ForceSMTPS = c.BoolT("force-smtps")
|
||||||
|
}
|
||||||
|
if c.IsSet("skip-verify") {
|
||||||
|
conf.SkipVerify = c.BoolT("skip-verify")
|
||||||
|
}
|
||||||
|
if c.IsSet("helo-hostname") {
|
||||||
|
conf.HeloHostname = c.String("helo-hostname")
|
||||||
|
}
|
||||||
|
if c.IsSet("disable-helo") {
|
||||||
|
conf.DisableHelo = c.BoolT("disable-helo")
|
||||||
|
}
|
||||||
|
if c.IsSet("skip-local-2fa") {
|
||||||
|
conf.SkipLocalTwoFA = c.BoolT("skip-local-2fa")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAddSMTP(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.IsSet("name") || len(c.String("name")) == 0 {
|
||||||
|
return errors.New("name must be set")
|
||||||
|
}
|
||||||
|
if !c.IsSet("host") || len(c.String("host")) == 0 {
|
||||||
|
return errors.New("host must be set")
|
||||||
|
}
|
||||||
|
if !c.IsSet("port") {
|
||||||
|
return errors.New("port must be set")
|
||||||
|
}
|
||||||
|
active := true
|
||||||
|
if c.IsSet("active") {
|
||||||
|
active = c.BoolT("active")
|
||||||
|
}
|
||||||
|
|
||||||
|
var smtpConfig smtp.Source
|
||||||
|
if err := parseSMTPConfig(c, &smtpConfig); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not set default to PLAIN
|
||||||
|
if len(smtpConfig.Auth) == 0 {
|
||||||
|
smtpConfig.Auth = "PLAIN"
|
||||||
|
}
|
||||||
|
|
||||||
|
return auth_model.CreateSource(&auth_model.Source{
|
||||||
|
Type: auth_model.SMTP,
|
||||||
|
Name: c.String("name"),
|
||||||
|
IsActive: active,
|
||||||
|
Cfg: &smtpConfig,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func runUpdateSMTP(c *cli.Context) error {
|
||||||
|
if !c.IsSet("id") {
|
||||||
|
return fmt.Errorf("--id flag is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
source, err := auth_model.GetSourceByID(c.Int64("id"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
smtpConfig := source.Cfg.(*smtp.Source)
|
||||||
|
|
||||||
|
if err := parseSMTPConfig(c, smtpConfig); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.IsSet("name") {
|
||||||
|
source.Name = c.String("name")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.IsSet("active") {
|
||||||
|
source.IsActive = c.BoolT("active")
|
||||||
|
}
|
||||||
|
|
||||||
|
source.Cfg = smtpConfig
|
||||||
|
|
||||||
|
return auth_model.UpdateSource(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runListAuth(c *cli.Context) error {
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
authSources, err := auth_model.Sources()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := tabwriter.AlignRight
|
||||||
|
if c.Bool("vertical-bars") {
|
||||||
|
flags |= tabwriter.Debug
|
||||||
|
}
|
||||||
|
|
||||||
|
padChar := byte('\t')
|
||||||
|
if len(c.String("pad-char")) > 0 {
|
||||||
|
padChar = c.String("pad-char")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// loop through each source and print
|
||||||
|
w := tabwriter.NewWriter(os.Stdout, c.Int("min-width"), c.Int("tab-width"), c.Int("padding"), padChar, flags)
|
||||||
|
fmt.Fprintf(w, "ID\tName\tType\tEnabled\n")
|
||||||
|
for _, source := range authSources {
|
||||||
|
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, source.Type.String(), source.IsActive)
|
||||||
|
}
|
||||||
|
w.Flush()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runDeleteAuth(c *cli.Context) error {
|
||||||
|
if !c.IsSet("id") {
|
||||||
|
return fmt.Errorf("--id flag is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
source, err := auth_model.GetSourceByID(c.Int64("id"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return auth_service.DeleteSource(source)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,111 +0,0 @@
|
|||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"text/tabwriter"
|
|
||||||
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
|
||||||
auth_service "code.gitea.io/gitea/services/auth"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
microcmdAuthDelete = &cli.Command{
|
|
||||||
Name: "delete",
|
|
||||||
Usage: "Delete specific auth source",
|
|
||||||
Flags: []cli.Flag{idFlag},
|
|
||||||
Action: runDeleteAuth,
|
|
||||||
}
|
|
||||||
microcmdAuthList = &cli.Command{
|
|
||||||
Name: "list",
|
|
||||||
Usage: "List auth sources",
|
|
||||||
Action: runListAuth,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.IntFlag{
|
|
||||||
Name: "min-width",
|
|
||||||
Usage: "Minimal cell width including any padding for the formatted table",
|
|
||||||
Value: 0,
|
|
||||||
},
|
|
||||||
&cli.IntFlag{
|
|
||||||
Name: "tab-width",
|
|
||||||
Usage: "width of tab characters in formatted table (equivalent number of spaces)",
|
|
||||||
Value: 8,
|
|
||||||
},
|
|
||||||
&cli.IntFlag{
|
|
||||||
Name: "padding",
|
|
||||||
Usage: "padding added to a cell before computing its width",
|
|
||||||
Value: 1,
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "pad-char",
|
|
||||||
Usage: `ASCII char used for padding if padchar == '\\t', the Writer will assume that the width of a '\\t' in the formatted output is tabwidth, and cells are left-aligned independent of align_left (for correct-looking results, tabwidth must correspond to the tab width in the viewer displaying the result)`,
|
|
||||||
Value: "\t",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "vertical-bars",
|
|
||||||
Usage: "Set to true to print vertical bars between columns",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func runListAuth(c *cli.Context) error {
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
authSources, err := db.Find[auth_model.Source](ctx, auth_model.FindSourcesOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
flags := tabwriter.AlignRight
|
|
||||||
if c.Bool("vertical-bars") {
|
|
||||||
flags |= tabwriter.Debug
|
|
||||||
}
|
|
||||||
|
|
||||||
padChar := byte('\t')
|
|
||||||
if len(c.String("pad-char")) > 0 {
|
|
||||||
padChar = c.String("pad-char")[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// loop through each source and print
|
|
||||||
w := tabwriter.NewWriter(os.Stdout, c.Int("min-width"), c.Int("tab-width"), c.Int("padding"), padChar, flags)
|
|
||||||
fmt.Fprintf(w, "ID\tName\tType\tEnabled\n")
|
|
||||||
for _, source := range authSources {
|
|
||||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, source.Type.String(), source.IsActive)
|
|
||||||
}
|
|
||||||
w.Flush()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func runDeleteAuth(c *cli.Context) error {
|
|
||||||
if !c.IsSet("id") {
|
|
||||||
return errors.New("--id flag is missing")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return auth_service.DeleteSource(ctx, source)
|
|
||||||
}
|
|
||||||
@@ -9,162 +9,133 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/auth"
|
"code.gitea.io/gitea/models/auth"
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
"code.gitea.io/gitea/services/auth/source/ldap"
|
"code.gitea.io/gitea/services/auth/source/ldap"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
authService struct {
|
authService struct {
|
||||||
initDB func(ctx context.Context) error
|
initDB func(ctx context.Context) error
|
||||||
createAuthSource func(context.Context, *auth.Source) error
|
createAuthSource func(*auth.Source) error
|
||||||
updateAuthSource func(context.Context, *auth.Source) error
|
updateAuthSource func(*auth.Source) error
|
||||||
getAuthSourceByID func(ctx context.Context, id int64) (*auth.Source, error)
|
getAuthSourceByID func(id int64) (*auth.Source, error)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
commonLdapCLIFlags = []cli.Flag{
|
commonLdapCLIFlags = []cli.Flag{
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
Usage: "Authentication name.",
|
Usage: "Authentication name.",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "not-active",
|
Name: "not-active",
|
||||||
Usage: "Deactivate the authentication source.",
|
Usage: "Deactivate the authentication source.",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "active",
|
Name: "active",
|
||||||
Usage: "Activate the authentication source.",
|
Usage: "Activate the authentication source.",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "security-protocol",
|
Name: "security-protocol",
|
||||||
Usage: "Security protocol name.",
|
Usage: "Security protocol name.",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "skip-tls-verify",
|
Name: "skip-tls-verify",
|
||||||
Usage: "Disable TLS verification.",
|
Usage: "Disable TLS verification.",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "host",
|
Name: "host",
|
||||||
Usage: "The address where the LDAP server can be reached.",
|
Usage: "The address where the LDAP server can be reached.",
|
||||||
},
|
},
|
||||||
&cli.IntFlag{
|
cli.IntFlag{
|
||||||
Name: "port",
|
Name: "port",
|
||||||
Usage: "The port to use when connecting to the LDAP server.",
|
Usage: "The port to use when connecting to the LDAP server.",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "user-search-base",
|
Name: "user-search-base",
|
||||||
Usage: "The LDAP base at which user accounts will be searched for.",
|
Usage: "The LDAP base at which user accounts will be searched for.",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "user-filter",
|
Name: "user-filter",
|
||||||
Usage: "An LDAP filter declaring how to find the user record that is attempting to authenticate.",
|
Usage: "An LDAP filter declaring how to find the user record that is attempting to authenticate.",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "admin-filter",
|
Name: "admin-filter",
|
||||||
Usage: "An LDAP filter specifying if a user should be given administrator privileges.",
|
Usage: "An LDAP filter specifying if a user should be given administrator privileges.",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "restricted-filter",
|
Name: "restricted-filter",
|
||||||
Usage: "An LDAP filter specifying if a user should be given restricted status.",
|
Usage: "An LDAP filter specifying if a user should be given restricted status.",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "allow-deactivate-all",
|
Name: "allow-deactivate-all",
|
||||||
Usage: "Allow empty search results to deactivate all users.",
|
Usage: "Allow empty search results to deactivate all users.",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "username-attribute",
|
Name: "username-attribute",
|
||||||
Usage: "The attribute of the user’s LDAP record containing the user name.",
|
Usage: "The attribute of the user’s LDAP record containing the user name.",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "firstname-attribute",
|
Name: "firstname-attribute",
|
||||||
Usage: "The attribute of the user’s LDAP record containing the user’s first name.",
|
Usage: "The attribute of the user’s LDAP record containing the user’s first name.",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "surname-attribute",
|
Name: "surname-attribute",
|
||||||
Usage: "The attribute of the user’s LDAP record containing the user’s surname.",
|
Usage: "The attribute of the user’s LDAP record containing the user’s surname.",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "email-attribute",
|
Name: "email-attribute",
|
||||||
Usage: "The attribute of the user’s LDAP record containing the user’s email address.",
|
Usage: "The attribute of the user’s LDAP record containing the user’s email address.",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "public-ssh-key-attribute",
|
Name: "public-ssh-key-attribute",
|
||||||
Usage: "The attribute of the user’s LDAP record containing the user’s public ssh key.",
|
Usage: "The attribute of the user’s LDAP record containing the user’s public ssh key.",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "skip-local-2fa",
|
Name: "skip-local-2fa",
|
||||||
Usage: "Set to true to skip local 2fa for users authenticated by this source",
|
Usage: "Set to true to skip local 2fa for users authenticated by this source",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "avatar-attribute",
|
Name: "avatar-attribute",
|
||||||
Usage: "The attribute of the user’s LDAP record containing the user’s avatar.",
|
Usage: "The attribute of the user’s LDAP record containing the user’s avatar.",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
ldapBindDnCLIFlags = append(commonLdapCLIFlags,
|
ldapBindDnCLIFlags = append(commonLdapCLIFlags,
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "bind-dn",
|
Name: "bind-dn",
|
||||||
Usage: "The DN to bind to the LDAP server with when searching for the user.",
|
Usage: "The DN to bind to the LDAP server with when searching for the user.",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "bind-password",
|
Name: "bind-password",
|
||||||
Usage: "The password for the Bind DN, if any.",
|
Usage: "The password for the Bind DN, if any.",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "attributes-in-bind",
|
Name: "attributes-in-bind",
|
||||||
Usage: "Fetch attributes in bind DN context.",
|
Usage: "Fetch attributes in bind DN context.",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "synchronize-users",
|
Name: "synchronize-users",
|
||||||
Usage: "Enable user synchronization.",
|
Usage: "Enable user synchronization.",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "disable-synchronize-users",
|
Name: "disable-synchronize-users",
|
||||||
Usage: "Disable user synchronization.",
|
Usage: "Disable user synchronization.",
|
||||||
},
|
},
|
||||||
&cli.UintFlag{
|
cli.UintFlag{
|
||||||
Name: "page-size",
|
Name: "page-size",
|
||||||
Usage: "Search page size.",
|
Usage: "Search page size.",
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "enable-groups",
|
|
||||||
Usage: "Enable LDAP groups",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "group-search-base-dn",
|
|
||||||
Usage: "The LDAP base DN at which group accounts will be searched for",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "group-member-attribute",
|
|
||||||
Usage: "Group attribute containing list of users",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "group-user-attribute",
|
|
||||||
Usage: "User attribute listed in group",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "group-filter",
|
|
||||||
Usage: "Verify group membership in LDAP",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "group-team-map",
|
|
||||||
Usage: "Map LDAP groups to Organization teams",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "group-team-map-removal",
|
|
||||||
Usage: "Remove users from synchronized teams if user does not belong to corresponding LDAP group",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
ldapSimpleAuthCLIFlags = append(commonLdapCLIFlags,
|
ldapSimpleAuthCLIFlags = append(commonLdapCLIFlags,
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "user-dn",
|
Name: "user-dn",
|
||||||
Usage: "The user's DN.",
|
Usage: "The user’s DN.",
|
||||||
})
|
})
|
||||||
|
|
||||||
microcmdAuthAddLdapBindDn = &cli.Command{
|
cmdAuthAddLdapBindDn = cli.Command{
|
||||||
Name: "add-ldap",
|
Name: "add-ldap",
|
||||||
Usage: "Add new LDAP (via Bind DN) authentication source",
|
Usage: "Add new LDAP (via Bind DN) authentication source",
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
@@ -173,7 +144,7 @@ var (
|
|||||||
Flags: ldapBindDnCLIFlags,
|
Flags: ldapBindDnCLIFlags,
|
||||||
}
|
}
|
||||||
|
|
||||||
microcmdAuthUpdateLdapBindDn = &cli.Command{
|
cmdAuthUpdateLdapBindDn = cli.Command{
|
||||||
Name: "update-ldap",
|
Name: "update-ldap",
|
||||||
Usage: "Update existing LDAP (via Bind DN) authentication source",
|
Usage: "Update existing LDAP (via Bind DN) authentication source",
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
@@ -182,7 +153,7 @@ var (
|
|||||||
Flags: append([]cli.Flag{idFlag}, ldapBindDnCLIFlags...),
|
Flags: append([]cli.Flag{idFlag}, ldapBindDnCLIFlags...),
|
||||||
}
|
}
|
||||||
|
|
||||||
microcmdAuthAddLdapSimpleAuth = &cli.Command{
|
cmdAuthAddLdapSimpleAuth = cli.Command{
|
||||||
Name: "add-ldap-simple",
|
Name: "add-ldap-simple",
|
||||||
Usage: "Add new LDAP (simple auth) authentication source",
|
Usage: "Add new LDAP (simple auth) authentication source",
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
@@ -191,7 +162,7 @@ var (
|
|||||||
Flags: ldapSimpleAuthCLIFlags,
|
Flags: ldapSimpleAuthCLIFlags,
|
||||||
}
|
}
|
||||||
|
|
||||||
microcmdAuthUpdateLdapSimpleAuth = &cli.Command{
|
cmdAuthUpdateLdapSimpleAuth = cli.Command{
|
||||||
Name: "update-ldap-simple",
|
Name: "update-ldap-simple",
|
||||||
Usage: "Update existing LDAP (simple auth) authentication source",
|
Usage: "Update existing LDAP (simple auth) authentication source",
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
@@ -211,8 +182,8 @@ func newAuthService() *authService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseAuthSourceLdap assigns values on authSource according to command line flags.
|
// parseAuthSource assigns values on authSource according to command line flags.
|
||||||
func parseAuthSourceLdap(c *cli.Context, authSource *auth.Source) {
|
func parseAuthSource(c *cli.Context, authSource *auth.Source) {
|
||||||
if c.IsSet("name") {
|
if c.IsSet("name") {
|
||||||
authSource.Name = c.String("name")
|
authSource.Name = c.String("name")
|
||||||
}
|
}
|
||||||
@@ -228,7 +199,6 @@ func parseAuthSourceLdap(c *cli.Context, authSource *auth.Source) {
|
|||||||
if c.IsSet("disable-synchronize-users") {
|
if c.IsSet("disable-synchronize-users") {
|
||||||
authSource.IsSyncEnabled = !c.Bool("disable-synchronize-users")
|
authSource.IsSyncEnabled = !c.Bool("disable-synchronize-users")
|
||||||
}
|
}
|
||||||
authSource.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseLdapConfig assigns values on config according to command line flags.
|
// parseLdapConfig assigns values on config according to command line flags.
|
||||||
@@ -300,26 +270,8 @@ func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
|
|||||||
if c.IsSet("allow-deactivate-all") {
|
if c.IsSet("allow-deactivate-all") {
|
||||||
config.AllowDeactivateAll = c.Bool("allow-deactivate-all")
|
config.AllowDeactivateAll = c.Bool("allow-deactivate-all")
|
||||||
}
|
}
|
||||||
if c.IsSet("enable-groups") {
|
if c.IsSet("skip-local-2fa") {
|
||||||
config.GroupsEnabled = c.Bool("enable-groups")
|
config.SkipLocalTwoFA = c.Bool("skip-local-2fa")
|
||||||
}
|
|
||||||
if c.IsSet("group-search-base-dn") {
|
|
||||||
config.GroupDN = c.String("group-search-base-dn")
|
|
||||||
}
|
|
||||||
if c.IsSet("group-member-attribute") {
|
|
||||||
config.GroupMemberUID = c.String("group-member-attribute")
|
|
||||||
}
|
|
||||||
if c.IsSet("group-user-attribute") {
|
|
||||||
config.UserUID = c.String("group-user-attribute")
|
|
||||||
}
|
|
||||||
if c.IsSet("group-filter") {
|
|
||||||
config.GroupFilter = c.String("group-filter")
|
|
||||||
}
|
|
||||||
if c.IsSet("group-team-map") {
|
|
||||||
config.GroupTeamMap = c.String("group-team-map")
|
|
||||||
}
|
|
||||||
if c.IsSet("group-team-map-removal") {
|
|
||||||
config.GroupTeamMapRemoval = c.Bool("group-team-map-removal")
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -337,12 +289,12 @@ func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
|
|||||||
|
|
||||||
// getAuthSource gets the login source by its id defined in the command line flags.
|
// getAuthSource 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.
|
// 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) getAuthSource(ctx context.Context, c *cli.Context, authType auth.Type) (*auth.Source, error) {
|
func (a *authService) getAuthSource(c *cli.Context, authType auth.Type) (*auth.Source, error) {
|
||||||
if err := argsSet(c, "id"); err != nil {
|
if err := argsSet(c, "id"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
authSource, err := a.getAuthSourceByID(ctx, c.Int64("id"))
|
authSource, err := a.getAuthSourceByID(c.Int64("id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -375,12 +327,12 @@ func (a *authService) addLdapBindDn(c *cli.Context) error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
parseAuthSourceLdap(c, authSource)
|
parseAuthSource(c, authSource)
|
||||||
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.createAuthSource(ctx, authSource)
|
return a.createAuthSource(authSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateLdapBindDn updates a new LDAP via Bind DN authentication source.
|
// updateLdapBindDn updates a new LDAP via Bind DN authentication source.
|
||||||
@@ -392,17 +344,17 @@ func (a *authService) updateLdapBindDn(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
authSource, err := a.getAuthSource(ctx, c, auth.LDAP)
|
authSource, err := a.getAuthSource(c, auth.LDAP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
parseAuthSourceLdap(c, authSource)
|
parseAuthSource(c, authSource)
|
||||||
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.updateAuthSource(ctx, authSource)
|
return a.updateAuthSource(authSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
|
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
|
||||||
@@ -426,15 +378,15 @@ func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
parseAuthSourceLdap(c, authSource)
|
parseAuthSource(c, authSource)
|
||||||
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.createAuthSource(ctx, authSource)
|
return a.createAuthSource(authSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateLdapSimpleAuth updates a new LDAP (simple auth) authentication source.
|
// updateLdapBindDn updates a new LDAP (simple auth) authentication source.
|
||||||
func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
|
func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
ctx, cancel := installSignals()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -443,15 +395,15 @@ func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
authSource, err := a.getAuthSource(ctx, c, auth.DLDAP)
|
authSource, err := a.getAuthSource(c, auth.DLDAP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
parseAuthSourceLdap(c, authSource)
|
parseAuthSource(c, authSource)
|
||||||
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.updateAuthSource(ctx, authSource)
|
return a.updateAuthSource(authSource)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"code.gitea.io/gitea/services/auth/source/ldap"
|
"code.gitea.io/gitea/services/auth/source/ldap"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAddLdapBindDn(t *testing.T) {
|
func TestAddLdapBindDn(t *testing.T) {
|
||||||
@@ -51,13 +51,6 @@ func TestAddLdapBindDn(t *testing.T) {
|
|||||||
"--attributes-in-bind",
|
"--attributes-in-bind",
|
||||||
"--synchronize-users",
|
"--synchronize-users",
|
||||||
"--page-size", "99",
|
"--page-size", "99",
|
||||||
"--enable-groups",
|
|
||||||
"--group-search-base-dn", "ou=group,dc=full-domain-bind,dc=org",
|
|
||||||
"--group-member-attribute", "memberUid",
|
|
||||||
"--group-user-attribute", "uid",
|
|
||||||
"--group-filter", "(|(cn=gitea_users)(cn=admins))",
|
|
||||||
"--group-team-map", `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`,
|
|
||||||
"--group-team-map-removal",
|
|
||||||
},
|
},
|
||||||
source: &auth.Source{
|
source: &auth.Source{
|
||||||
Type: auth.LDAP,
|
Type: auth.LDAP,
|
||||||
@@ -85,13 +78,6 @@ func TestAddLdapBindDn(t *testing.T) {
|
|||||||
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
|
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
|
||||||
RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)",
|
RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)",
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
GroupsEnabled: true,
|
|
||||||
GroupDN: "ou=group,dc=full-domain-bind,dc=org",
|
|
||||||
GroupMemberUID: "memberUid",
|
|
||||||
UserUID: "uid",
|
|
||||||
GroupFilter: "(|(cn=gitea_users)(cn=admins))",
|
|
||||||
GroupTeamMap: `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`,
|
|
||||||
GroupTeamMapRemoval: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -224,23 +210,23 @@ func TestAddLdapBindDn(t *testing.T) {
|
|||||||
initDB: func(context.Context) error {
|
initDB: func(context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
createAuthSource: func(ctx context.Context, authSource *auth.Source) error {
|
createAuthSource: func(authSource *auth.Source) error {
|
||||||
createdAuthSource = authSource
|
createdAuthSource = authSource
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
|
updateAuthSource: func(authSource *auth.Source) error {
|
||||||
assert.FailNow(t, "updateAuthSource called", "case %d: should not call updateAuthSource", n)
|
assert.FailNow(t, "case %d: should not call updateAuthSource", n)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) {
|
getAuthSourceByID: func(id int64) (*auth.Source, error) {
|
||||||
assert.FailNow(t, "getAuthSourceByID called", "case %d: should not call getAuthSourceByID", n)
|
assert.FailNow(t, "case %d: should not call getAuthSourceByID", n)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a copy of command to test
|
// Create a copy of command to test
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.Flags = microcmdAuthAddLdapBindDn.Flags
|
app.Flags = cmdAuthAddLdapBindDn.Flags
|
||||||
app.Action = service.addLdapBindDn
|
app.Action = service.addLdapBindDn
|
||||||
|
|
||||||
// Run it
|
// Run it
|
||||||
@@ -455,23 +441,23 @@ func TestAddLdapSimpleAuth(t *testing.T) {
|
|||||||
initDB: func(context.Context) error {
|
initDB: func(context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
createAuthSource: func(ctx context.Context, authSource *auth.Source) error {
|
createAuthSource: func(authSource *auth.Source) error {
|
||||||
createdAuthSource = authSource
|
createdAuthSource = authSource
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
|
updateAuthSource: func(authSource *auth.Source) error {
|
||||||
assert.FailNow(t, "updateAuthSource called", "case %d: should not call updateAuthSource", n)
|
assert.FailNow(t, "case %d: should not call updateAuthSource", n)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) {
|
getAuthSourceByID: func(id int64) (*auth.Source, error) {
|
||||||
assert.FailNow(t, "getAuthSourceById called", "case %d: should not call getAuthSourceByID", n)
|
assert.FailNow(t, "case %d: should not call getAuthSourceByID", n)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a copy of command to test
|
// Create a copy of command to test
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.Flags = microcmdAuthAddLdapSimpleAuth.Flags
|
app.Flags = cmdAuthAddLdapSimpleAuth.Flags
|
||||||
app.Action = service.addLdapSimpleAuth
|
app.Action = service.addLdapSimpleAuth
|
||||||
|
|
||||||
// Run it
|
// Run it
|
||||||
@@ -524,13 +510,6 @@ func TestUpdateLdapBindDn(t *testing.T) {
|
|||||||
"--bind-password", "secret-bind-full",
|
"--bind-password", "secret-bind-full",
|
||||||
"--synchronize-users",
|
"--synchronize-users",
|
||||||
"--page-size", "99",
|
"--page-size", "99",
|
||||||
"--enable-groups",
|
|
||||||
"--group-search-base-dn", "ou=group,dc=full-domain-bind,dc=org",
|
|
||||||
"--group-member-attribute", "memberUid",
|
|
||||||
"--group-user-attribute", "uid",
|
|
||||||
"--group-filter", "(|(cn=gitea_users)(cn=admins))",
|
|
||||||
"--group-team-map", `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`,
|
|
||||||
"--group-team-map-removal",
|
|
||||||
},
|
},
|
||||||
id: 23,
|
id: 23,
|
||||||
existingAuthSource: &auth.Source{
|
existingAuthSource: &auth.Source{
|
||||||
@@ -566,13 +545,6 @@ func TestUpdateLdapBindDn(t *testing.T) {
|
|||||||
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
|
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
|
||||||
RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)",
|
RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)",
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
GroupsEnabled: true,
|
|
||||||
GroupDN: "ou=group,dc=full-domain-bind,dc=org",
|
|
||||||
GroupMemberUID: "memberUid",
|
|
||||||
UserUID: "uid",
|
|
||||||
GroupFilter: "(|(cn=gitea_users)(cn=admins))",
|
|
||||||
GroupTeamMap: `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`,
|
|
||||||
GroupTeamMapRemoval: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -924,15 +896,15 @@ func TestUpdateLdapBindDn(t *testing.T) {
|
|||||||
initDB: func(context.Context) error {
|
initDB: func(context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
createAuthSource: func(ctx context.Context, authSource *auth.Source) error {
|
createAuthSource: func(authSource *auth.Source) error {
|
||||||
assert.FailNow(t, "createAuthSource called", "case %d: should not call createAuthSource", n)
|
assert.FailNow(t, "case %d: should not call createAuthSource", n)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
|
updateAuthSource: func(authSource *auth.Source) error {
|
||||||
updatedAuthSource = authSource
|
updatedAuthSource = authSource
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) {
|
getAuthSourceByID: func(id int64) (*auth.Source, error) {
|
||||||
if c.id != 0 {
|
if c.id != 0 {
|
||||||
assert.Equal(t, c.id, id, "case %d: wrong id", n)
|
assert.Equal(t, c.id, id, "case %d: wrong id", n)
|
||||||
}
|
}
|
||||||
@@ -948,7 +920,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
|
|||||||
|
|
||||||
// Create a copy of command to test
|
// Create a copy of command to test
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.Flags = microcmdAuthUpdateLdapBindDn.Flags
|
app.Flags = cmdAuthUpdateLdapBindDn.Flags
|
||||||
app.Action = service.updateLdapBindDn
|
app.Action = service.updateLdapBindDn
|
||||||
|
|
||||||
// Run it
|
// Run it
|
||||||
@@ -1314,15 +1286,15 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
|
|||||||
initDB: func(context.Context) error {
|
initDB: func(context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
createAuthSource: func(ctx context.Context, authSource *auth.Source) error {
|
createAuthSource: func(authSource *auth.Source) error {
|
||||||
assert.FailNow(t, "createAuthSource called", "case %d: should not call createAuthSource", n)
|
assert.FailNow(t, "case %d: should not call createAuthSource", n)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
|
updateAuthSource: func(authSource *auth.Source) error {
|
||||||
updatedAuthSource = authSource
|
updatedAuthSource = authSource
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) {
|
getAuthSourceByID: func(id int64) (*auth.Source, error) {
|
||||||
if c.id != 0 {
|
if c.id != 0 {
|
||||||
assert.Equal(t, c.id, id, "case %d: wrong id", n)
|
assert.Equal(t, c.id, id, "case %d: wrong id", n)
|
||||||
}
|
}
|
||||||
@@ -1338,7 +1310,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
|
|||||||
|
|
||||||
// Create a copy of command to test
|
// Create a copy of command to test
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.Flags = microcmdAuthUpdateLdapSimpleAuth.Flags
|
app.Flags = cmdAuthUpdateLdapSimpleAuth.Flags
|
||||||
app.Action = service.updateLdapSimpleAuth
|
app.Action = service.updateLdapSimpleAuth
|
||||||
|
|
||||||
// Run it
|
// Run it
|
||||||
|
|||||||
@@ -1,300 +0,0 @@
|
|||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
"code.gitea.io/gitea/services/auth/source/oauth2"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
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-tenant-id",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Use custom Tenant ID for 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)",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "icon-url",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Custom icon URL for OAuth2 login source",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "skip-local-2fa",
|
|
||||||
Usage: "Set to true to skip local 2fa for users authenticated by this source",
|
|
||||||
},
|
|
||||||
&cli.StringSliceFlag{
|
|
||||||
Name: "scopes",
|
|
||||||
Value: nil,
|
|
||||||
Usage: "Scopes to request when to authenticate against this OAuth2 source",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "required-claim-name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Claim name that has to be set to allow users to login with this source",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "required-claim-value",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Claim value that has to be set to allow users to login with this source",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "group-claim-name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Claim name providing group names for this source",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "admin-group",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Group Claim value for administrator users",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "restricted-group",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Group Claim value for restricted users",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "group-team-map",
|
|
||||||
Value: "",
|
|
||||||
Usage: "JSON mapping between groups and org teams",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "group-team-map-removal",
|
|
||||||
Usage: "Activate automatic team membership removal depending on groups",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdAuthAddOauth = &cli.Command{
|
|
||||||
Name: "add-oauth",
|
|
||||||
Usage: "Add new Oauth authentication source",
|
|
||||||
Action: runAddOauth,
|
|
||||||
Flags: oauthCLIFlags,
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdAuthUpdateOauth = &cli.Command{
|
|
||||||
Name: "update-oauth",
|
|
||||||
Usage: "Update existing Oauth authentication source",
|
|
||||||
Action: runUpdateOauth,
|
|
||||||
Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func parseOAuth2Config(c *cli.Context) *oauth2.Source {
|
|
||||||
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"),
|
|
||||||
Tenant: c.String("custom-tenant-id"),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
customURLMapping = nil
|
|
||||||
}
|
|
||||||
return &oauth2.Source{
|
|
||||||
Provider: c.String("provider"),
|
|
||||||
ClientID: c.String("key"),
|
|
||||||
ClientSecret: c.String("secret"),
|
|
||||||
OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
|
|
||||||
CustomURLMapping: customURLMapping,
|
|
||||||
IconURL: c.String("icon-url"),
|
|
||||||
Scopes: c.StringSlice("scopes"),
|
|
||||||
RequiredClaimName: c.String("required-claim-name"),
|
|
||||||
RequiredClaimValue: c.String("required-claim-value"),
|
|
||||||
GroupClaimName: c.String("group-claim-name"),
|
|
||||||
AdminGroup: c.String("admin-group"),
|
|
||||||
RestrictedGroup: c.String("restricted-group"),
|
|
||||||
GroupTeamMap: c.String("group-team-map"),
|
|
||||||
GroupTeamMapRemoval: c.Bool("group-team-map-removal"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runAddOauth(c *cli.Context) error {
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
config := parseOAuth2Config(c)
|
|
||||||
if config.Provider == "openidConnect" {
|
|
||||||
discoveryURL, err := url.Parse(config.OpenIDConnectAutoDiscoveryURL)
|
|
||||||
if err != nil || (discoveryURL.Scheme != "http" && discoveryURL.Scheme != "https") {
|
|
||||||
return fmt.Errorf("invalid Auto Discovery URL: %s (this must be a valid URL starting with http:// or https://)", config.OpenIDConnectAutoDiscoveryURL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return auth_model.CreateSource(ctx, &auth_model.Source{
|
|
||||||
Type: auth_model.OAuth2,
|
|
||||||
Name: c.String("name"),
|
|
||||||
IsActive: true,
|
|
||||||
Cfg: config,
|
|
||||||
TwoFactorPolicy: util.Iif(c.Bool("skip-local-2fa"), "skip", ""),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func runUpdateOauth(c *cli.Context) error {
|
|
||||||
if !c.IsSet("id") {
|
|
||||||
return errors.New("--id flag is missing")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
oAuth2Config := source.Cfg.(*oauth2.Source)
|
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("icon-url") {
|
|
||||||
oAuth2Config.IconURL = c.String("icon-url")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("scopes") {
|
|
||||||
oAuth2Config.Scopes = c.StringSlice("scopes")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("required-claim-name") {
|
|
||||||
oAuth2Config.RequiredClaimName = c.String("required-claim-name")
|
|
||||||
}
|
|
||||||
if c.IsSet("required-claim-value") {
|
|
||||||
oAuth2Config.RequiredClaimValue = c.String("required-claim-value")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("group-claim-name") {
|
|
||||||
oAuth2Config.GroupClaimName = c.String("group-claim-name")
|
|
||||||
}
|
|
||||||
if c.IsSet("admin-group") {
|
|
||||||
oAuth2Config.AdminGroup = c.String("admin-group")
|
|
||||||
}
|
|
||||||
if c.IsSet("restricted-group") {
|
|
||||||
oAuth2Config.RestrictedGroup = c.String("restricted-group")
|
|
||||||
}
|
|
||||||
if c.IsSet("group-team-map") {
|
|
||||||
oAuth2Config.GroupTeamMap = c.String("group-team-map")
|
|
||||||
}
|
|
||||||
if c.IsSet("group-team-map-removal") {
|
|
||||||
oAuth2Config.GroupTeamMapRemoval = c.Bool("group-team-map-removal")
|
|
||||||
}
|
|
||||||
|
|
||||||
// update custom URL mapping
|
|
||||||
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
|
|
||||||
customURLMapping.Tenant = oAuth2Config.CustomURLMapping.Tenant
|
|
||||||
}
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("use-custom-urls") && c.IsSet("custom-tenant-id") {
|
|
||||||
customURLMapping.Tenant = c.String("custom-tenant-id")
|
|
||||||
}
|
|
||||||
|
|
||||||
oAuth2Config.CustomURLMapping = customURLMapping
|
|
||||||
source.Cfg = oAuth2Config
|
|
||||||
source.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
|
|
||||||
return auth_model.UpdateSource(ctx, source)
|
|
||||||
}
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
"code.gitea.io/gitea/services/auth/source/smtp"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
smtpCLIFlags = []cli.Flag{
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Application Name",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "auth-type",
|
|
||||||
Value: "PLAIN",
|
|
||||||
Usage: "SMTP Authentication Type (PLAIN/LOGIN/CRAM-MD5) default PLAIN",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "host",
|
|
||||||
Value: "",
|
|
||||||
Usage: "SMTP Host",
|
|
||||||
},
|
|
||||||
&cli.IntFlag{
|
|
||||||
Name: "port",
|
|
||||||
Usage: "SMTP Port",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "force-smtps",
|
|
||||||
Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.",
|
|
||||||
Value: true,
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "skip-verify",
|
|
||||||
Usage: "Skip TLS verify.",
|
|
||||||
Value: true,
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "helo-hostname",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Hostname sent with HELO. Leave blank to send current hostname",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "disable-helo",
|
|
||||||
Usage: "Disable SMTP helo.",
|
|
||||||
Value: true,
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "allowed-domains",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Leave empty to allow all domains. Separate multiple domains with a comma (',')",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "skip-local-2fa",
|
|
||||||
Usage: "Skip 2FA to log on.",
|
|
||||||
Value: true,
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "active",
|
|
||||||
Usage: "This Authentication Source is Activated.",
|
|
||||||
Value: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdAuthAddSMTP = &cli.Command{
|
|
||||||
Name: "add-smtp",
|
|
||||||
Usage: "Add new SMTP authentication source",
|
|
||||||
Action: runAddSMTP,
|
|
||||||
Flags: smtpCLIFlags,
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdAuthUpdateSMTP = &cli.Command{
|
|
||||||
Name: "update-smtp",
|
|
||||||
Usage: "Update existing SMTP authentication source",
|
|
||||||
Action: runUpdateSMTP,
|
|
||||||
Flags: append(smtpCLIFlags[:1], append([]cli.Flag{idFlag}, smtpCLIFlags[1:]...)...),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
|
|
||||||
if c.IsSet("auth-type") {
|
|
||||||
conf.Auth = c.String("auth-type")
|
|
||||||
validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"}
|
|
||||||
if !util.SliceContainsString(validAuthTypes, strings.ToUpper(c.String("auth-type"))) {
|
|
||||||
return errors.New("Auth must be one of PLAIN/LOGIN/CRAM-MD5")
|
|
||||||
}
|
|
||||||
conf.Auth = c.String("auth-type")
|
|
||||||
}
|
|
||||||
if c.IsSet("host") {
|
|
||||||
conf.Host = c.String("host")
|
|
||||||
}
|
|
||||||
if c.IsSet("port") {
|
|
||||||
conf.Port = c.Int("port")
|
|
||||||
}
|
|
||||||
if c.IsSet("allowed-domains") {
|
|
||||||
conf.AllowedDomains = c.String("allowed-domains")
|
|
||||||
}
|
|
||||||
if c.IsSet("force-smtps") {
|
|
||||||
conf.ForceSMTPS = c.Bool("force-smtps")
|
|
||||||
}
|
|
||||||
if c.IsSet("skip-verify") {
|
|
||||||
conf.SkipVerify = c.Bool("skip-verify")
|
|
||||||
}
|
|
||||||
if c.IsSet("helo-hostname") {
|
|
||||||
conf.HeloHostname = c.String("helo-hostname")
|
|
||||||
}
|
|
||||||
if c.IsSet("disable-helo") {
|
|
||||||
conf.DisableHelo = c.Bool("disable-helo")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func runAddSMTP(c *cli.Context) error {
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.IsSet("name") || len(c.String("name")) == 0 {
|
|
||||||
return errors.New("name must be set")
|
|
||||||
}
|
|
||||||
if !c.IsSet("host") || len(c.String("host")) == 0 {
|
|
||||||
return errors.New("host must be set")
|
|
||||||
}
|
|
||||||
if !c.IsSet("port") {
|
|
||||||
return errors.New("port must be set")
|
|
||||||
}
|
|
||||||
active := true
|
|
||||||
if c.IsSet("active") {
|
|
||||||
active = c.Bool("active")
|
|
||||||
}
|
|
||||||
|
|
||||||
var smtpConfig smtp.Source
|
|
||||||
if err := parseSMTPConfig(c, &smtpConfig); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If not set default to PLAIN
|
|
||||||
if len(smtpConfig.Auth) == 0 {
|
|
||||||
smtpConfig.Auth = "PLAIN"
|
|
||||||
}
|
|
||||||
|
|
||||||
return auth_model.CreateSource(ctx, &auth_model.Source{
|
|
||||||
Type: auth_model.SMTP,
|
|
||||||
Name: c.String("name"),
|
|
||||||
IsActive: active,
|
|
||||||
Cfg: &smtpConfig,
|
|
||||||
TwoFactorPolicy: util.Iif(c.Bool("skip-local-2fa"), "skip", ""),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func runUpdateSMTP(c *cli.Context) error {
|
|
||||||
if !c.IsSet("id") {
|
|
||||||
return errors.New("--id flag is missing")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
smtpConfig := source.Cfg.(*smtp.Source)
|
|
||||||
|
|
||||||
if err := parseSMTPConfig(c, smtpConfig); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("name") {
|
|
||||||
source.Name = c.String("name")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("active") {
|
|
||||||
source.IsActive = c.Bool("active")
|
|
||||||
}
|
|
||||||
|
|
||||||
source.Cfg = smtpConfig
|
|
||||||
source.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
|
|
||||||
return auth_model.UpdateSource(ctx, source)
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"code.gitea.io/gitea/modules/graceful"
|
|
||||||
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
microcmdRegenHooks = &cli.Command{
|
|
||||||
Name: "hooks",
|
|
||||||
Usage: "Regenerate git-hooks",
|
|
||||||
Action: runRegenerateHooks,
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdRegenKeys = &cli.Command{
|
|
||||||
Name: "keys",
|
|
||||||
Usage: "Regenerate authorized_keys file",
|
|
||||||
Action: runRegenerateKeys,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func runRegenerateHooks(_ *cli.Context) error {
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
|
|
||||||
}
|
|
||||||
|
|
||||||
func runRegenerateKeys(_ *cli.Context) error {
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return asymkey_service.RewriteAllPublicKeys(ctx)
|
|
||||||
}
|
|
||||||
@@ -4,13 +4,13 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var subcmdUser = &cli.Command{
|
var subcmdUser = cli.Command{
|
||||||
Name: "user",
|
Name: "user",
|
||||||
Usage: "Modify users",
|
Usage: "Modify users",
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []cli.Command{
|
||||||
microcmdUserCreate,
|
microcmdUserCreate,
|
||||||
microcmdUserList,
|
microcmdUserList,
|
||||||
microcmdUserChangePassword,
|
microcmdUserChangePassword,
|
||||||
|
|||||||
@@ -4,39 +4,31 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/auth/password"
|
pwd "code.gitea.io/gitea/modules/auth/password"
|
||||||
"code.gitea.io/gitea/modules/optional"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
user_service "code.gitea.io/gitea/services/user"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var microcmdUserChangePassword = &cli.Command{
|
var microcmdUserChangePassword = cli.Command{
|
||||||
Name: "change-password",
|
Name: "change-password",
|
||||||
Usage: "Change a user's password",
|
Usage: "Change a user's password",
|
||||||
Action: runChangePassword,
|
Action: runChangePassword,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "username",
|
Name: "username,u",
|
||||||
Aliases: []string{"u"},
|
Value: "",
|
||||||
Value: "",
|
Usage: "The user to change password for",
|
||||||
Usage: "The user to change password for",
|
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "password",
|
Name: "password,p",
|
||||||
Aliases: []string{"p"},
|
Value: "",
|
||||||
Value: "",
|
Usage: "New password to set for user",
|
||||||
Usage: "New password to set for user",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "must-change-password",
|
|
||||||
Usage: "User must change password (can be disabled by --must-change-password=false)",
|
|
||||||
Value: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -52,27 +44,31 @@ func runChangePassword(c *cli.Context) error {
|
|||||||
if err := initDB(ctx); err != nil {
|
if err := initDB(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(c.String("password")) < setting.MinPasswordLength {
|
||||||
|
return fmt.Errorf("Password is not long enough. Needs to be at least %d", setting.MinPasswordLength)
|
||||||
|
}
|
||||||
|
|
||||||
user, err := user_model.GetUserByName(ctx, c.String("username"))
|
if !pwd.IsComplexEnough(c.String("password")) {
|
||||||
|
return errors.New("Password does not meet complexity requirements")
|
||||||
|
}
|
||||||
|
pwned, err := pwd.IsPwned(context.Background(), c.String("password"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if pwned {
|
||||||
opts := &user_service.UpdateAuthOptions{
|
return errors.New("The password you chose is on a list of stolen passwords previously exposed in public data breaches. Please try again with a different password.\nFor more details, see https://haveibeenpwned.com/Passwords")
|
||||||
Password: optional.Some(c.String("password")),
|
|
||||||
MustChangePassword: optional.Some(c.Bool("must-change-password")),
|
|
||||||
}
|
}
|
||||||
if err := user_service.UpdateAuth(ctx, user, opts); err != nil {
|
uname := c.String("username")
|
||||||
switch {
|
user, err := user_model.GetUserByName(ctx, uname)
|
||||||
case errors.Is(err, password.ErrMinLength):
|
if err != nil {
|
||||||
return fmt.Errorf("password is not long enough, needs to be at least %d characters", setting.MinPasswordLength)
|
return err
|
||||||
case errors.Is(err, password.ErrComplexity):
|
}
|
||||||
return errors.New("password does not meet complexity requirements")
|
if err = user.SetPassword(c.String("password")); err != nil {
|
||||||
case errors.Is(err, password.ErrIsPwned):
|
return err
|
||||||
return errors.New("the password is in a list of stolen passwords previously exposed in public data breaches, please try again with a different password, to see more details: https://haveibeenpwned.com/Passwords")
|
}
|
||||||
default:
|
|
||||||
return err
|
if err = user_model.UpdateUserCols(ctx, user, "passwd", "passwd_hash_algo", "salt"); err != nil {
|
||||||
}
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s's password has been successfully updated!\n", user.Name)
|
fmt.Printf("%s's password has been successfully updated!\n", user.Name)
|
||||||
|
|||||||
@@ -4,120 +4,78 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"os"
|
||||||
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
"code.gitea.io/gitea/models/db"
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
pwd "code.gitea.io/gitea/modules/auth/password"
|
pwd "code.gitea.io/gitea/modules/auth/password"
|
||||||
"code.gitea.io/gitea/modules/optional"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var microcmdUserCreate = &cli.Command{
|
var microcmdUserCreate = cli.Command{
|
||||||
Name: "create",
|
Name: "create",
|
||||||
Usage: "Create a new user in database",
|
Usage: "Create a new user in database",
|
||||||
Action: runCreateUser,
|
Action: runCreateUser,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
Usage: "Username. DEPRECATED: use username instead",
|
Usage: "Username. DEPRECATED: use username instead",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "username",
|
Name: "username",
|
||||||
Usage: "Username",
|
Usage: "Username",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "user-type",
|
|
||||||
Usage: "Set user's type: individual or bot",
|
|
||||||
Value: "individual",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "password",
|
Name: "password",
|
||||||
Usage: "User password",
|
Usage: "User password",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "email",
|
Name: "email",
|
||||||
Usage: "User email address",
|
Usage: "User email address",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
Usage: "User is an admin",
|
Usage: "User is an admin",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "random-password",
|
Name: "random-password",
|
||||||
Usage: "Generate a random password for the user",
|
Usage: "Generate a random password for the user",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "must-change-password",
|
Name: "must-change-password",
|
||||||
Usage: "User must change password after initial login, defaults to true for all users except the first one (can be disabled by --must-change-password=false)",
|
Usage: "Set this option to false to prevent forcing the user to change their password after initial login, (Default: true)",
|
||||||
DisableDefaultText: true,
|
|
||||||
},
|
},
|
||||||
&cli.IntFlag{
|
cli.IntFlag{
|
||||||
Name: "random-password-length",
|
Name: "random-password-length",
|
||||||
Usage: "Length of the random password to be generated",
|
Usage: "Length of the random password to be generated",
|
||||||
Value: 12,
|
Value: 12,
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "access-token",
|
Name: "access-token",
|
||||||
Usage: "Generate access token for the user",
|
Usage: "Generate access token for the user",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.BoolFlag{
|
||||||
Name: "access-token-name",
|
|
||||||
Usage: `Name of the generated access token`,
|
|
||||||
Value: "gitea-admin",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "access-token-scopes",
|
|
||||||
Usage: `Scopes of the generated access token, comma separated. Examples: "all", "public-only,read:issue", "write:repository,write:user"`,
|
|
||||||
Value: "all",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "restricted",
|
Name: "restricted",
|
||||||
Usage: "Make a restricted user account",
|
Usage: "Make a restricted user account",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "fullname",
|
|
||||||
Usage: `The full, human-readable name of the user`,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCreateUser(c *cli.Context) error {
|
func runCreateUser(c *cli.Context) error {
|
||||||
// this command highly depends on the many setting options (create org, visibility, etc.), so it must have a full setting load first
|
|
||||||
// duplicate setting loading should be safe at the moment, but it should be refactored & improved in the future.
|
|
||||||
setting.LoadSettings()
|
|
||||||
|
|
||||||
if err := argsSet(c, "email"); err != nil {
|
if err := argsSet(c, "email"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
userTypes := map[string]user_model.UserType{
|
|
||||||
"individual": user_model.UserTypeIndividual,
|
|
||||||
"bot": user_model.UserTypeBot,
|
|
||||||
}
|
|
||||||
userType, ok := userTypes[c.String("user-type")]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("invalid user type: %s", c.String("user-type"))
|
|
||||||
}
|
|
||||||
if userType != user_model.UserTypeIndividual {
|
|
||||||
// Some other commands like "change-password" also only support individual users.
|
|
||||||
// It needs to clarify the "password" behavior for bot users in the future.
|
|
||||||
// At the moment, we do not allow setting password for bot users.
|
|
||||||
if c.IsSet("password") || c.IsSet("random-password") {
|
|
||||||
return errors.New("password can only be set for individual users")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if c.IsSet("name") && c.IsSet("username") {
|
if c.IsSet("name") && c.IsSet("username") {
|
||||||
return errors.New("cannot set both --name and --username flags")
|
return errors.New("Cannot set both --name and --username flags")
|
||||||
}
|
}
|
||||||
if !c.IsSet("name") && !c.IsSet("username") {
|
if !c.IsSet("name") && !c.IsSet("username") {
|
||||||
return errors.New("one of --name or --username flags must be set")
|
return errors.New("One of --name or --username flags must be set")
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.IsSet("password") && c.IsSet("random-password") {
|
if c.IsSet("password") && c.IsSet("random-password") {
|
||||||
@@ -129,19 +87,14 @@ func runCreateUser(c *cli.Context) error {
|
|||||||
username = c.String("username")
|
username = c.String("username")
|
||||||
} else {
|
} else {
|
||||||
username = c.String("name")
|
username = c.String("name")
|
||||||
_, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n")
|
fmt.Fprintf(os.Stderr, "--name flag is deprecated. Use --username instead.\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := c.Context
|
ctx, cancel := installSignals()
|
||||||
if !setting.IsInTesting {
|
defer cancel()
|
||||||
// FIXME: need to refactor the "installSignals/initDB" related code later
|
|
||||||
// it doesn't make sense to call it in (almost) every command action function
|
if err := initDB(ctx); err != nil {
|
||||||
var cancel context.CancelFunc
|
return err
|
||||||
ctx, cancel = installSignals()
|
|
||||||
defer cancel()
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var password string
|
var password string
|
||||||
@@ -154,34 +107,27 @@ func runCreateUser(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("generated random password is '%s'\n", password)
|
fmt.Printf("generated random password is '%s'\n", password)
|
||||||
} else if userType == user_model.UserTypeIndividual {
|
} else {
|
||||||
return errors.New("must set either password or random-password flag")
|
return errors.New("must set either password or random-password flag")
|
||||||
}
|
}
|
||||||
|
|
||||||
isAdmin := c.Bool("admin")
|
// always default to true
|
||||||
mustChangePassword := true // always default to true
|
changePassword := true
|
||||||
if c.IsSet("must-change-password") {
|
|
||||||
if userType != user_model.UserTypeIndividual {
|
// If this is the first user being created.
|
||||||
return errors.New("must-change-password flag can only be set for individual users")
|
// Take it as the admin and don't force a password update.
|
||||||
}
|
if n := user_model.CountUsers(nil); n == 0 {
|
||||||
// if the flag is set, use the value provided by the user
|
changePassword = false
|
||||||
mustChangePassword = c.Bool("must-change-password")
|
|
||||||
} else if userType == user_model.UserTypeIndividual {
|
|
||||||
// check whether there are users in the database
|
|
||||||
hasUserRecord, err := db.IsTableNotEmpty(&user_model.User{})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("IsTableNotEmpty: %w", err)
|
|
||||||
}
|
|
||||||
if !hasUserRecord {
|
|
||||||
// if this is the first one being created, don't force to change password (keep the old behavior)
|
|
||||||
mustChangePassword = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
restricted := optional.None[bool]()
|
if c.IsSet("must-change-password") {
|
||||||
|
changePassword = c.Bool("must-change-password")
|
||||||
|
}
|
||||||
|
|
||||||
|
restricted := util.OptionalBoolNone
|
||||||
|
|
||||||
if c.IsSet("restricted") {
|
if c.IsSet("restricted") {
|
||||||
restricted = optional.Some(c.Bool("restricted"))
|
restricted = util.OptionalBoolOf(c.Bool("restricted"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// default user visibility in app.ini
|
// default user visibility in app.ini
|
||||||
@@ -190,53 +136,34 @@ func runCreateUser(c *cli.Context) error {
|
|||||||
u := &user_model.User{
|
u := &user_model.User{
|
||||||
Name: username,
|
Name: username,
|
||||||
Email: c.String("email"),
|
Email: c.String("email"),
|
||||||
IsAdmin: isAdmin,
|
|
||||||
Type: userType,
|
|
||||||
Passwd: password,
|
Passwd: password,
|
||||||
MustChangePassword: mustChangePassword,
|
IsAdmin: c.Bool("admin"),
|
||||||
|
MustChangePassword: changePassword,
|
||||||
Visibility: visibility,
|
Visibility: visibility,
|
||||||
FullName: c.String("fullname"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
||||||
IsActive: optional.Some(true),
|
IsActive: util.OptionalBoolTrue,
|
||||||
IsRestricted: restricted,
|
IsRestricted: restricted,
|
||||||
}
|
}
|
||||||
|
|
||||||
var accessTokenName string
|
if err := user_model.CreateUser(u, overwriteDefault); err != nil {
|
||||||
var accessTokenScope auth_model.AccessTokenScope
|
|
||||||
if c.IsSet("access-token") {
|
|
||||||
accessTokenName = strings.TrimSpace(c.String("access-token-name"))
|
|
||||||
if accessTokenName == "" {
|
|
||||||
return errors.New("access-token-name cannot be empty")
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
accessTokenScope, err = auth_model.AccessTokenScope(c.String("access-token-scopes")).Normalize()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("invalid access token scope provided: %w", err)
|
|
||||||
}
|
|
||||||
if !accessTokenScope.HasPermissionScope() {
|
|
||||||
return errors.New("access token does not have any permission")
|
|
||||||
}
|
|
||||||
} else if c.IsSet("access-token-name") || c.IsSet("access-token-scopes") {
|
|
||||||
return errors.New("access-token-name and access-token-scopes flags are only valid when access-token flag is set")
|
|
||||||
}
|
|
||||||
|
|
||||||
// arguments should be prepared before creating the user & access token, in case there is anything wrong
|
|
||||||
|
|
||||||
// create the user
|
|
||||||
if err := user_model.CreateUser(ctx, u, &user_model.Meta{}, overwriteDefault); err != nil {
|
|
||||||
return fmt.Errorf("CreateUser: %w", err)
|
return fmt.Errorf("CreateUser: %w", err)
|
||||||
}
|
}
|
||||||
fmt.Printf("New user '%s' has been successfully created!\n", username)
|
|
||||||
|
|
||||||
// create the access token
|
if c.Bool("access-token") {
|
||||||
if accessTokenScope != "" {
|
t := &auth_model.AccessToken{
|
||||||
t := &auth_model.AccessToken{Name: accessTokenName, UID: u.ID, Scope: accessTokenScope}
|
Name: "gitea-admin",
|
||||||
if err := auth_model.NewAccessToken(ctx, t); err != nil {
|
UID: u.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := auth_model.NewAccessToken(t); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Access token was successfully created... %s\n", t.Token)
|
fmt.Printf("Access token was successfully created... %s\n", t.Token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Printf("New user '%s' has been successfully created!\n", username)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
|
||||||
"code.gitea.io/gitea/models/unittest"
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAdminUserCreate(t *testing.T) {
|
|
||||||
app := NewMainApp(AppVersion{})
|
|
||||||
|
|
||||||
reset := func() {
|
|
||||||
require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
|
|
||||||
require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
|
|
||||||
require.NoError(t, db.TruncateBeans(db.DefaultContext, &auth_model.AccessToken{}))
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("MustChangePassword", func(t *testing.T) {
|
|
||||||
type check struct {
|
|
||||||
IsAdmin bool
|
|
||||||
MustChangePassword bool
|
|
||||||
}
|
|
||||||
createCheck := func(name, args string) check {
|
|
||||||
require.NoError(t, app.Run(strings.Fields(fmt.Sprintf("./gitea admin user create --username %s --email %s@gitea.local %s --password foobar", name, name, args))))
|
|
||||||
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: name})
|
|
||||||
return check{IsAdmin: u.IsAdmin, MustChangePassword: u.MustChangePassword}
|
|
||||||
}
|
|
||||||
reset()
|
|
||||||
assert.Equal(t, check{IsAdmin: false, MustChangePassword: false}, createCheck("u", ""), "first non-admin user doesn't need to change password")
|
|
||||||
|
|
||||||
reset()
|
|
||||||
assert.Equal(t, check{IsAdmin: true, MustChangePassword: false}, createCheck("u", "--admin"), "first admin user doesn't need to change password")
|
|
||||||
|
|
||||||
reset()
|
|
||||||
assert.Equal(t, check{IsAdmin: true, MustChangePassword: true}, createCheck("u", "--admin --must-change-password"))
|
|
||||||
assert.Equal(t, check{IsAdmin: true, MustChangePassword: true}, createCheck("u2", "--admin"))
|
|
||||||
assert.Equal(t, check{IsAdmin: true, MustChangePassword: false}, createCheck("u3", "--admin --must-change-password=false"))
|
|
||||||
assert.Equal(t, check{IsAdmin: false, MustChangePassword: true}, createCheck("u4", ""))
|
|
||||||
assert.Equal(t, check{IsAdmin: false, MustChangePassword: false}, createCheck("u5", "--must-change-password=false"))
|
|
||||||
})
|
|
||||||
|
|
||||||
createUser := func(name string, args ...string) error {
|
|
||||||
return app.Run(append([]string{"./gitea", "admin", "user", "create", "--username", name, "--email", name + "@gitea.local"}, args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("UserType", func(t *testing.T) {
|
|
||||||
reset()
|
|
||||||
assert.ErrorContains(t, createUser("u", "--user-type", "invalid"), "invalid user type")
|
|
||||||
assert.ErrorContains(t, createUser("u", "--user-type", "bot", "--password", "123"), "can only be set for individual users")
|
|
||||||
assert.ErrorContains(t, createUser("u", "--user-type", "bot", "--must-change-password"), "can only be set for individual users")
|
|
||||||
|
|
||||||
assert.NoError(t, createUser("u", "--user-type", "bot"))
|
|
||||||
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "u"})
|
|
||||||
assert.Equal(t, user_model.UserTypeBot, u.Type)
|
|
||||||
assert.Empty(t, u.Passwd)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("AccessToken", func(t *testing.T) {
|
|
||||||
// no generated access token
|
|
||||||
reset()
|
|
||||||
assert.NoError(t, createUser("u", "--random-password"))
|
|
||||||
assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{}))
|
|
||||||
assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
|
|
||||||
|
|
||||||
// using "--access-token" only means "all" access
|
|
||||||
reset()
|
|
||||||
assert.NoError(t, createUser("u", "--random-password", "--access-token"))
|
|
||||||
assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{}))
|
|
||||||
assert.Equal(t, 1, unittest.GetCount(t, &auth_model.AccessToken{}))
|
|
||||||
accessToken := unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{Name: "gitea-admin"})
|
|
||||||
hasScopes, err := accessToken.Scope.HasScope(auth_model.AccessTokenScopeWriteAdmin, auth_model.AccessTokenScopeWriteRepository)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.True(t, hasScopes)
|
|
||||||
|
|
||||||
// using "--access-token" with name & scopes
|
|
||||||
reset()
|
|
||||||
assert.NoError(t, createUser("u", "--random-password", "--access-token", "--access-token-name", "new-token-name", "--access-token-scopes", "read:issue,read:user"))
|
|
||||||
assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{}))
|
|
||||||
assert.Equal(t, 1, unittest.GetCount(t, &auth_model.AccessToken{}))
|
|
||||||
accessToken = unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{Name: "new-token-name"})
|
|
||||||
hasScopes, err = accessToken.Scope.HasScope(auth_model.AccessTokenScopeReadIssue, auth_model.AccessTokenScopeReadUser)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.True(t, hasScopes)
|
|
||||||
hasScopes, err = accessToken.Scope.HasScope(auth_model.AccessTokenScopeWriteAdmin, auth_model.AccessTokenScopeWriteRepository)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.False(t, hasScopes)
|
|
||||||
|
|
||||||
// using "--access-token-name" without "--access-token"
|
|
||||||
reset()
|
|
||||||
err = createUser("u", "--random-password", "--access-token-name", "new-token-name")
|
|
||||||
assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
|
|
||||||
assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
|
|
||||||
assert.ErrorContains(t, err, "access-token-name and access-token-scopes flags are only valid when access-token flag is set")
|
|
||||||
|
|
||||||
// using "--access-token-scopes" without "--access-token"
|
|
||||||
reset()
|
|
||||||
err = createUser("u", "--random-password", "--access-token-scopes", "read:issue")
|
|
||||||
assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
|
|
||||||
assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
|
|
||||||
assert.ErrorContains(t, err, "access-token-name and access-token-scopes flags are only valid when access-token flag is set")
|
|
||||||
|
|
||||||
// empty permission
|
|
||||||
reset()
|
|
||||||
err = createUser("u", "--random-password", "--access-token", "--access-token-scopes", "public-only")
|
|
||||||
assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
|
|
||||||
assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
|
|
||||||
assert.ErrorContains(t, err, "access token does not have any permission")
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("UserFields", func(t *testing.T) {
|
|
||||||
reset()
|
|
||||||
assert.NoError(t, createUser("u-FullNameWithSpace", "--random-password", "--fullname", "First O'Middle Last"))
|
|
||||||
unittest.AssertExistsAndLoadBean(t, &user_model.User{
|
|
||||||
Name: "u-FullNameWithSpace",
|
|
||||||
LowerName: "u-fullnamewithspace",
|
|
||||||
FullName: "First O'Middle Last",
|
|
||||||
Email: "u-FullNameWithSpace@gitea.local",
|
|
||||||
})
|
|
||||||
|
|
||||||
assert.NoError(t, createUser("u-FullNameEmpty", "--random-password", "--fullname", ""))
|
|
||||||
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "u-fullnameempty"})
|
|
||||||
assert.Empty(t, u.FullName)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -12,28 +11,26 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/storage"
|
"code.gitea.io/gitea/modules/storage"
|
||||||
user_service "code.gitea.io/gitea/services/user"
|
user_service "code.gitea.io/gitea/services/user"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var microcmdUserDelete = &cli.Command{
|
var microcmdUserDelete = cli.Command{
|
||||||
Name: "delete",
|
Name: "delete",
|
||||||
Usage: "Delete specific user by id, name or email",
|
Usage: "Delete specific user by id, name or email",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.Int64Flag{
|
cli.Int64Flag{
|
||||||
Name: "id",
|
Name: "id",
|
||||||
Usage: "ID of user of the user to delete",
|
Usage: "ID of user of the user to delete",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "username",
|
Name: "username,u",
|
||||||
Aliases: []string{"u"},
|
Usage: "Username of the user to delete",
|
||||||
Usage: "Username of the user to delete",
|
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "email",
|
Name: "email,e",
|
||||||
Aliases: []string{"e"},
|
Usage: "Email of the user to delete",
|
||||||
Usage: "Email of the user to delete",
|
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "purge",
|
Name: "purge",
|
||||||
Usage: "Purge user, all their repositories, organizations and comments",
|
Usage: "Purge user, all their repositories, organizations and comments",
|
||||||
},
|
},
|
||||||
@@ -43,7 +40,7 @@ var microcmdUserDelete = &cli.Command{
|
|||||||
|
|
||||||
func runDeleteUser(c *cli.Context) error {
|
func runDeleteUser(c *cli.Context) error {
|
||||||
if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
|
if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
|
||||||
return errors.New("You must provide the id, username or email of a user to delete")
|
return fmt.Errorf("You must provide the id, username or email of a user to delete")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
ctx, cancel := installSignals()
|
||||||
|
|||||||
@@ -4,38 +4,35 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
auth_model "code.gitea.io/gitea/models/auth"
|
auth_model "code.gitea.io/gitea/models/auth"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var microcmdUserGenerateAccessToken = &cli.Command{
|
var microcmdUserGenerateAccessToken = cli.Command{
|
||||||
Name: "generate-access-token",
|
Name: "generate-access-token",
|
||||||
Usage: "Generate an access token for a specific user",
|
Usage: "Generate an access token for a specific user",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "username",
|
Name: "username,u",
|
||||||
Aliases: []string{"u"},
|
Usage: "Username",
|
||||||
Usage: "Username",
|
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "token-name",
|
Name: "token-name,t",
|
||||||
Aliases: []string{"t"},
|
Usage: "Token name",
|
||||||
Usage: "Token name",
|
Value: "gitea-admin",
|
||||||
Value: "gitea-admin",
|
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "raw",
|
Name: "raw",
|
||||||
Usage: "Display only the token value",
|
Usage: "Display only the token value",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "scopes",
|
Name: "scopes",
|
||||||
Value: "all",
|
Value: "",
|
||||||
Usage: `Comma separated list of scopes to apply to access token, examples: "all", "public-only,read:issue", "write:repository,write:user"`,
|
Usage: "Comma separated list of scopes to apply to access token",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: runGenerateAccessToken,
|
Action: runGenerateAccessToken,
|
||||||
@@ -43,7 +40,7 @@ var microcmdUserGenerateAccessToken = &cli.Command{
|
|||||||
|
|
||||||
func runGenerateAccessToken(c *cli.Context) error {
|
func runGenerateAccessToken(c *cli.Context) error {
|
||||||
if !c.IsSet("username") {
|
if !c.IsSet("username") {
|
||||||
return errors.New("you must provide a username to generate a token for")
|
return fmt.Errorf("You must provide a username to generate a token for")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
ctx, cancel := installSignals()
|
||||||
@@ -64,12 +61,12 @@ func runGenerateAccessToken(c *cli.Context) error {
|
|||||||
UID: user.ID,
|
UID: user.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
exist, err := auth_model.AccessTokenByNameExists(ctx, t)
|
exist, err := auth_model.AccessTokenByNameExists(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if exist {
|
if exist {
|
||||||
return errors.New("access token name has been used already")
|
return fmt.Errorf("access token name has been used already")
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure the scopes are valid
|
// make sure the scopes are valid
|
||||||
@@ -77,13 +74,10 @@ func runGenerateAccessToken(c *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid access token scope provided: %w", err)
|
return fmt.Errorf("invalid access token scope provided: %w", err)
|
||||||
}
|
}
|
||||||
if !accessTokenScope.HasPermissionScope() {
|
|
||||||
return errors.New("access token does not have any permission")
|
|
||||||
}
|
|
||||||
t.Scope = accessTokenScope
|
t.Scope = accessTokenScope
|
||||||
|
|
||||||
// create the token
|
// create the token
|
||||||
if err := auth_model.NewAccessToken(ctx, t); err != nil {
|
if err := auth_model.NewAccessToken(t); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,15 +10,15 @@ import (
|
|||||||
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var microcmdUserList = &cli.Command{
|
var microcmdUserList = cli.Command{
|
||||||
Name: "list",
|
Name: "list",
|
||||||
Usage: "List users",
|
Usage: "List users",
|
||||||
Action: runListUsers,
|
Action: runListUsers,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
Usage: "List only admin users",
|
Usage: "List only admin users",
|
||||||
},
|
},
|
||||||
@@ -33,7 +33,7 @@ func runListUsers(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
users, err := user_model.GetAllUsers(ctx)
|
users, err := user_model.GetAllUsers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ func runListUsers(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
twofa := user_model.UserList(users).GetTwoFaStatus(ctx)
|
twofa := user_model.UserList(users).GetTwoFaStatus()
|
||||||
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n")
|
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n")
|
||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\t%t\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin, twofa[u.ID])
|
fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\t%t\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin, twofa[u.ID])
|
||||||
|
|||||||
@@ -9,25 +9,23 @@ import (
|
|||||||
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var microcmdUserMustChangePassword = &cli.Command{
|
var microcmdUserMustChangePassword = cli.Command{
|
||||||
Name: "must-change-password",
|
Name: "must-change-password",
|
||||||
Usage: "Set the must change password flag for the provided users or all users",
|
Usage: "Set the must change password flag for the provided users or all users",
|
||||||
Action: runMustChangePassword,
|
Action: runMustChangePassword,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "all",
|
Name: "all,A",
|
||||||
Aliases: []string{"A"},
|
Usage: "All users must change password, except those explicitly excluded with --exclude",
|
||||||
Usage: "All users must change password, except those explicitly excluded with --exclude",
|
|
||||||
},
|
},
|
||||||
&cli.StringSliceFlag{
|
cli.StringSliceFlag{
|
||||||
Name: "exclude",
|
Name: "exclude,e",
|
||||||
Aliases: []string{"e"},
|
Usage: "Do not change the must-change-password flag for these users",
|
||||||
Usage: "Do not change the must-change-password flag for these users",
|
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "unset",
|
Name: "unset",
|
||||||
Usage: "Instead of setting the must-change-password flag, unset it",
|
Usage: "Instead of setting the must-change-password flag, unset it",
|
||||||
},
|
},
|
||||||
@@ -50,7 +48,7 @@ func runMustChangePassword(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := user_model.SetMustChangePassword(ctx, all, mustChangePassword, c.Args().Slice(), exclude)
|
n, err := user_model.SetMustChangePassword(ctx, all, mustChangePassword, c.Args(), exclude)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
18
cmd/cert.go
18
cmd/cert.go
@@ -20,43 +20,43 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdCert represents the available cert sub-command.
|
// CmdCert represents the available cert sub-command.
|
||||||
var CmdCert = &cli.Command{
|
var CmdCert = cli.Command{
|
||||||
Name: "cert",
|
Name: "cert",
|
||||||
Usage: "Generate self-signed certificate",
|
Usage: "Generate self-signed certificate",
|
||||||
Description: `Generate a self-signed X.509 certificate for a TLS server.
|
Description: `Generate a self-signed X.509 certificate for a TLS server.
|
||||||
Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
|
Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
|
||||||
Action: runCert,
|
Action: runCert,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "host",
|
Name: "host",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "Comma-separated hostnames and IPs to generate a certificate for",
|
Usage: "Comma-separated hostnames and IPs to generate a certificate for",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "ecdsa-curve",
|
Name: "ecdsa-curve",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521",
|
Usage: "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521",
|
||||||
},
|
},
|
||||||
&cli.IntFlag{
|
cli.IntFlag{
|
||||||
Name: "rsa-bits",
|
Name: "rsa-bits",
|
||||||
Value: 3072,
|
Value: 2048,
|
||||||
Usage: "Size of RSA key to generate. Ignored if --ecdsa-curve is set",
|
Usage: "Size of RSA key to generate. Ignored if --ecdsa-curve is set",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "start-date",
|
Name: "start-date",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "Creation date formatted as Jan 1 15:04:05 2011",
|
Usage: "Creation date formatted as Jan 1 15:04:05 2011",
|
||||||
},
|
},
|
||||||
&cli.DurationFlag{
|
cli.DurationFlag{
|
||||||
Name: "duration",
|
Name: "duration",
|
||||||
Value: 365 * 24 * time.Hour,
|
Value: 365 * 24 * time.Hour,
|
||||||
Usage: "Duration that certificate is valid for",
|
Usage: "Duration that certificate is valid for",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "ca",
|
Name: "ca",
|
||||||
Usage: "whether this cert should be its own Certificate Authority",
|
Usage: "whether this cert should be its own Certificate Authority",
|
||||||
},
|
},
|
||||||
|
|||||||
15
cmd/cmd.go
15
cmd/cmd.go
@@ -20,7 +20,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// argsSet checks that all the required arguments are set. args is a list of
|
// argsSet checks that all the required arguments are set. args is a list of
|
||||||
@@ -109,24 +109,15 @@ func setupConsoleLogger(level log.Level, colorize bool, out io.Writer) {
|
|||||||
log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer)
|
log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func globalBool(c *cli.Context, name string) bool {
|
|
||||||
for _, ctx := range c.Lineage() {
|
|
||||||
if ctx.Bool(name) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrepareConsoleLoggerLevel by default, use INFO level for console logger, but some sub-commands (for git/ssh protocol) shouldn't output any log to stdout.
|
// PrepareConsoleLoggerLevel by default, use INFO level for console logger, but some sub-commands (for git/ssh protocol) shouldn't output any log to stdout.
|
||||||
// Any log appears in git stdout pipe will break the git protocol, eg: client can't push and hangs forever.
|
// Any log appears in git stdout pipe will break the git protocol, eg: client can't push and hangs forever.
|
||||||
func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(*cli.Context) error {
|
func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(*cli.Context) error {
|
||||||
return func(c *cli.Context) error {
|
return func(c *cli.Context) error {
|
||||||
level := defaultLevel
|
level := defaultLevel
|
||||||
if globalBool(c, "quiet") {
|
if c.Bool("quiet") || c.GlobalBoolT("quiet") {
|
||||||
level = log.FATAL
|
level = log.FATAL
|
||||||
}
|
}
|
||||||
if globalBool(c, "debug") || globalBool(c, "verbose") {
|
if c.Bool("debug") || c.GlobalBool("debug") || c.Bool("verbose") || c.GlobalBool("verbose") {
|
||||||
level = log.TRACE
|
level = log.TRACE
|
||||||
}
|
}
|
||||||
log.SetConsoleLogger(log.DEFAULT, "console-default", level)
|
log.SetConsoleLogger(log.DEFAULT, "console-default", level)
|
||||||
|
|||||||
56
cmd/convert.go
Normal file
56
cmd/convert.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/db"
|
||||||
|
"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 or MSSQL database from varchar to nvarchar",
|
||||||
|
Action: runConvert,
|
||||||
|
}
|
||||||
|
|
||||||
|
func runConvert(ctx *cli.Context) error {
|
||||||
|
stdCtx, cancel := installSignals()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := initDB(stdCtx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("AppPath: %s", setting.AppPath)
|
||||||
|
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
||||||
|
log.Info("Custom path: %s", setting.CustomPath)
|
||||||
|
log.Info("Log path: %s", setting.Log.RootPath)
|
||||||
|
log.Info("Configuration file: %s", setting.CustomConf)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case setting.Database.Type.IsMySQL():
|
||||||
|
if err := db.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")
|
||||||
|
case setting.Database.Type.IsMSSQL():
|
||||||
|
if err := db.ConvertVarcharToNVarchar(); err != nil {
|
||||||
|
log.Fatal("Failed to convert database from varchar to nvarchar: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("Converted successfully, please confirm your database's all columns character is NVARCHAR now")
|
||||||
|
default:
|
||||||
|
fmt.Println("This command can only be used with a MySQL or MSSQL database")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -8,11 +8,11 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdDocs represents the available docs sub-command.
|
// CmdDocs represents the available docs sub-command.
|
||||||
var CmdDocs = &cli.Command{
|
var CmdDocs = cli.Command{
|
||||||
Name: "docs",
|
Name: "docs",
|
||||||
Usage: "Output CLI documentation",
|
Usage: "Output CLI documentation",
|
||||||
Description: "A command to output Gitea's CLI documentation, optionally to a file.",
|
Description: "A command to output Gitea's CLI documentation, optionally to a file.",
|
||||||
@@ -23,9 +23,8 @@ var CmdDocs = &cli.Command{
|
|||||||
Usage: "Output man pages instead",
|
Usage: "Output man pages instead",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "output",
|
Name: "output, o",
|
||||||
Aliases: []string{"o"},
|
Usage: "Path to output to instead of stdout (will overwrite if exists)",
|
||||||
Usage: "Path to output to instead of stdout (will overwrite if exists)",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
100
cmd/doctor.go
100
cmd/doctor.go
@@ -4,7 +4,6 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
golog "log"
|
golog "log"
|
||||||
"os"
|
"os"
|
||||||
@@ -15,72 +14,61 @@ import (
|
|||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/migrations"
|
"code.gitea.io/gitea/models/migrations"
|
||||||
migrate_base "code.gitea.io/gitea/models/migrations/base"
|
migrate_base "code.gitea.io/gitea/models/migrations/base"
|
||||||
"code.gitea.io/gitea/modules/container"
|
"code.gitea.io/gitea/modules/doctor"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/services/doctor"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdDoctor represents the available doctor sub-command.
|
// CmdDoctor represents the available doctor sub-command.
|
||||||
var CmdDoctor = &cli.Command{
|
var CmdDoctor = cli.Command{
|
||||||
Name: "doctor",
|
Name: "doctor",
|
||||||
Usage: "Diagnose and optionally fix problems, convert or re-create database tables",
|
|
||||||
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
|
|
||||||
|
|
||||||
Subcommands: []*cli.Command{
|
|
||||||
cmdDoctorCheck,
|
|
||||||
cmdRecreateTable,
|
|
||||||
cmdDoctorConvert,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmdDoctorCheck = &cli.Command{
|
|
||||||
Name: "check",
|
|
||||||
Usage: "Diagnose and optionally fix problems",
|
Usage: "Diagnose and optionally fix problems",
|
||||||
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
|
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
|
||||||
Action: runDoctorCheck,
|
Action: runDoctor,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "list",
|
Name: "list",
|
||||||
Usage: "List the available checks",
|
Usage: "List the available checks",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "default",
|
Name: "default",
|
||||||
Usage: "Run the default checks (if neither --run or --all is set, this is the default behaviour)",
|
Usage: "Run the default checks (if neither --run or --all is set, this is the default behaviour)",
|
||||||
},
|
},
|
||||||
&cli.StringSliceFlag{
|
cli.StringSliceFlag{
|
||||||
Name: "run",
|
Name: "run",
|
||||||
Usage: "Run the provided checks - (if --default is set, the default checks will also run)",
|
Usage: "Run the provided checks - (if --default is set, the default checks will also run)",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "all",
|
Name: "all",
|
||||||
Usage: "Run all the available checks",
|
Usage: "Run all the available checks",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "fix",
|
Name: "fix",
|
||||||
Usage: "Automatically fix what we can",
|
Usage: "Automatically fix what we can",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "log-file",
|
Name: "log-file",
|
||||||
Usage: `Name of the log file (no verbose log output by default). Set to "-" to output to stdout`,
|
Usage: `Name of the log file (default: "doctor.log"). Set to "-" to output to stdout, set to "" to disable`,
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "color",
|
Name: "color, H",
|
||||||
Aliases: []string{"H"},
|
Usage: "Use color for outputted information",
|
||||||
Usage: "Use color for outputted information",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Subcommands: []cli.Command{
|
||||||
|
cmdRecreateTable,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmdRecreateTable = &cli.Command{
|
var cmdRecreateTable = cli.Command{
|
||||||
Name: "recreate-table",
|
Name: "recreate-table",
|
||||||
Usage: "Recreate tables from XORM definitions and copy the data.",
|
Usage: "Recreate tables from XORM definitions and copy the data.",
|
||||||
ArgsUsage: "[TABLE]... : (TABLEs to recreate - leave blank for all)",
|
ArgsUsage: "[TABLE]... : (TABLEs to recreate - leave blank for all)",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
Usage: "Print SQL commands sent",
|
Usage: "Print SQL commands sent",
|
||||||
},
|
},
|
||||||
@@ -131,8 +119,8 @@ func runRecreateTable(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
recreateTables := migrate_base.RecreateTables(beans...)
|
recreateTables := migrate_base.RecreateTables(beans...)
|
||||||
|
|
||||||
return db.InitEngineWithMigration(stdCtx, func(ctx context.Context, x *xorm.Engine) error {
|
return db.InitEngineWithMigration(stdCtx, func(x *xorm.Engine) error {
|
||||||
if err := migrations.EnsureUpToDate(ctx, x); err != nil {
|
if err := migrations.EnsureUpToDate(x); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return recreateTables(x)
|
return recreateTables(x)
|
||||||
@@ -144,12 +132,18 @@ func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) {
|
|||||||
setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr)
|
setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr)
|
||||||
|
|
||||||
logFile := ctx.String("log-file")
|
logFile := ctx.String("log-file")
|
||||||
switch logFile {
|
if !ctx.IsSet("log-file") {
|
||||||
case "":
|
logFile = "doctor.log"
|
||||||
return // if no doctor log-file is set, do not show any log from default logger
|
}
|
||||||
case "-":
|
|
||||||
|
if len(logFile) == 0 {
|
||||||
|
// if no doctor log-file is set, do not show any log from default logger
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if logFile == "-" {
|
||||||
setupConsoleLogger(log.TRACE, colorize, os.Stdout)
|
setupConsoleLogger(log.TRACE, colorize, os.Stdout)
|
||||||
default:
|
} else {
|
||||||
logFile, _ = filepath.Abs(logFile)
|
logFile, _ = filepath.Abs(logFile)
|
||||||
writeMode := log.WriterMode{Level: log.TRACE, WriterOption: log.WriterFileOption{FileName: logFile}}
|
writeMode := log.WriterMode{Level: log.TRACE, WriterOption: log.WriterFileOption{FileName: logFile}}
|
||||||
writer, err := log.NewEventWriter("console-to-file", "file", writeMode)
|
writer, err := log.NewEventWriter("console-to-file", "file", writeMode)
|
||||||
@@ -161,7 +155,7 @@ func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDoctorCheck(ctx *cli.Context) error {
|
func runDoctor(ctx *cli.Context) error {
|
||||||
stdCtx, cancel := installSignals()
|
stdCtx, cancel := installSignals()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
@@ -180,7 +174,6 @@ func runDoctorCheck(ctx *cli.Context) error {
|
|||||||
if ctx.IsSet("list") {
|
if ctx.IsSet("list") {
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
|
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
|
||||||
_, _ = w.Write([]byte("Default\tName\tTitle\n"))
|
_, _ = w.Write([]byte("Default\tName\tTitle\n"))
|
||||||
doctor.SortChecks(doctor.Checks)
|
|
||||||
for _, check := range doctor.Checks {
|
for _, check := range doctor.Checks {
|
||||||
if check.IsDefault {
|
if check.IsDefault {
|
||||||
_, _ = w.Write([]byte{'*'})
|
_, _ = w.Write([]byte{'*'})
|
||||||
@@ -196,19 +189,25 @@ func runDoctorCheck(ctx *cli.Context) error {
|
|||||||
|
|
||||||
var checks []*doctor.Check
|
var checks []*doctor.Check
|
||||||
if ctx.Bool("all") {
|
if ctx.Bool("all") {
|
||||||
checks = make([]*doctor.Check, len(doctor.Checks))
|
checks = doctor.Checks
|
||||||
copy(checks, doctor.Checks)
|
|
||||||
} else if ctx.IsSet("run") {
|
} else if ctx.IsSet("run") {
|
||||||
addDefault := ctx.Bool("default")
|
addDefault := ctx.Bool("default")
|
||||||
runNamesSet := container.SetOf(ctx.StringSlice("run")...)
|
names := ctx.StringSlice("run")
|
||||||
for _, check := range doctor.Checks {
|
for i, name := range names {
|
||||||
if (addDefault && check.IsDefault) || runNamesSet.Contains(check.Name) {
|
names[i] = strings.ToLower(strings.TrimSpace(name))
|
||||||
checks = append(checks, check)
|
|
||||||
runNamesSet.Remove(check.Name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if len(runNamesSet) > 0 {
|
|
||||||
return fmt.Errorf("unknown checks: %q", strings.Join(runNamesSet.Values(), ","))
|
for _, check := range doctor.Checks {
|
||||||
|
if addDefault && check.IsDefault {
|
||||||
|
checks = append(checks, check)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, name := range names {
|
||||||
|
if name == check.Name {
|
||||||
|
checks = append(checks, check)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, check := range doctor.Checks {
|
for _, check := range doctor.Checks {
|
||||||
@@ -217,5 +216,6 @@ func runDoctorCheck(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return doctor.RunChecks(stdCtx, colorize, ctx.Bool("fix"), checks)
|
return doctor.RunChecks(stdCtx, colorize, ctx.Bool("fix"), checks)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// cmdDoctorConvert represents the available convert sub-command.
|
|
||||||
var cmdDoctorConvert = &cli.Command{
|
|
||||||
Name: "convert",
|
|
||||||
Usage: "Convert the database",
|
|
||||||
Description: "A command to convert an existing MySQL database from utf8 to utf8mb4 or MSSQL database from varchar to nvarchar",
|
|
||||||
Action: runDoctorConvert,
|
|
||||||
}
|
|
||||||
|
|
||||||
func runDoctorConvert(ctx *cli.Context) error {
|
|
||||||
stdCtx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(stdCtx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("AppPath: %s", setting.AppPath)
|
|
||||||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
|
||||||
log.Info("Custom path: %s", setting.CustomPath)
|
|
||||||
log.Info("Log path: %s", setting.Log.RootPath)
|
|
||||||
log.Info("Configuration file: %s", setting.CustomConf)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case setting.Database.Type.IsMySQL():
|
|
||||||
if err := db.ConvertDatabaseTable(); err != nil {
|
|
||||||
log.Fatal("Failed to convert database & table: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println("Converted successfully, please confirm your database's character set is now utf8mb4")
|
|
||||||
case setting.Database.Type.IsMSSQL():
|
|
||||||
if err := db.ConvertVarcharToNVarchar(); err != nil {
|
|
||||||
log.Fatal("Failed to convert database from varchar to nvarchar: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println("Converted successfully, please confirm your database's all columns character is NVARCHAR now")
|
|
||||||
default:
|
|
||||||
fmt.Println("This command can only be used with a MySQL or MSSQL database")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/services/doctor"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDoctorRun(t *testing.T) {
|
|
||||||
doctor.Register(&doctor.Check{
|
|
||||||
Title: "Test Check",
|
|
||||||
Name: "test-check",
|
|
||||||
Run: func(ctx context.Context, logger log.Logger, autofix bool) error { return nil },
|
|
||||||
|
|
||||||
SkipDatabaseInitialization: true,
|
|
||||||
})
|
|
||||||
app := cli.NewApp()
|
|
||||||
app.Commands = []*cli.Command{cmdDoctorCheck}
|
|
||||||
err := app.Run([]string{"./gitea", "check", "--run", "test-check"})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
err = app.Run([]string{"./gitea", "check", "--run", "no-such"})
|
|
||||||
assert.ErrorContains(t, err, `unknown checks: "no-such"`)
|
|
||||||
err = app.Run([]string{"./gitea", "check", "--run", "test-check,no-such"})
|
|
||||||
assert.ErrorContains(t, err, `unknown checks: "no-such"`)
|
|
||||||
}
|
|
||||||
418
cmd/dump.go
418
cmd/dump.go
@@ -5,13 +5,15 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/modules/dump"
|
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
@@ -20,122 +22,195 @@ import (
|
|||||||
|
|
||||||
"gitea.com/go-chi/session"
|
"gitea.com/go-chi/session"
|
||||||
"github.com/mholt/archiver/v3"
|
"github.com/mholt/archiver/v3"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func addReader(w archiver.Writer, r io.ReadCloser, info os.FileInfo, customName string, verbose bool) error {
|
||||||
|
if verbose {
|
||||||
|
log.Info("Adding file %s", customName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return w.Write(archiver.File{
|
||||||
|
FileInfo: archiver.FileInfo{
|
||||||
|
FileInfo: info,
|
||||||
|
CustomName: customName,
|
||||||
|
},
|
||||||
|
ReadCloser: r,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFile(w archiver.Writer, filePath, absPath string, verbose bool) error {
|
||||||
|
file, err := os.Open(absPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
fileInfo, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return addReader(w, file, fileInfo, filePath, verbose)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSubdir(upper, lower string) (bool, error) {
|
||||||
|
if relPath, err := filepath.Rel(upper, lower); err != nil {
|
||||||
|
return false, err
|
||||||
|
} else if relPath == "." || !strings.HasPrefix(relPath, ".") {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type outputType struct {
|
||||||
|
Enum []string
|
||||||
|
Default string
|
||||||
|
selected string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o outputType) Join() string {
|
||||||
|
return strings.Join(o.Enum, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *outputType) Set(value string) error {
|
||||||
|
for _, enum := range o.Enum {
|
||||||
|
if enum == value {
|
||||||
|
o.selected = value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("allowed values are %s", o.Join())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o outputType) String() string {
|
||||||
|
if o.selected == "" {
|
||||||
|
return o.Default
|
||||||
|
}
|
||||||
|
return o.selected
|
||||||
|
}
|
||||||
|
|
||||||
|
var outputTypeEnum = &outputType{
|
||||||
|
Enum: []string{"zip", "tar", "tar.sz", "tar.gz", "tar.xz", "tar.bz2", "tar.br", "tar.lz4", "tar.zst"},
|
||||||
|
Default: "zip",
|
||||||
|
}
|
||||||
|
|
||||||
// CmdDump represents the available dump sub-command.
|
// CmdDump represents the available dump sub-command.
|
||||||
var CmdDump = &cli.Command{
|
var CmdDump = cli.Command{
|
||||||
Name: "dump",
|
Name: "dump",
|
||||||
Usage: "Dump Gitea files and database",
|
Usage: "Dump Gitea files and database",
|
||||||
Description: `Dump compresses all related files and database into zip file. It can be used for backup and capture Gitea server image to send to maintainer`,
|
Description: `Dump compresses all related files and database into zip file.
|
||||||
Action: runDump,
|
It can be used for backup and capture Gitea server image to send to maintainer`,
|
||||||
|
Action: runDump,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "file",
|
Name: "file, f",
|
||||||
Aliases: []string{"f"},
|
Value: fmt.Sprintf("gitea-dump-%d.zip", time.Now().Unix()),
|
||||||
Usage: `Name of the dump file which will be created, default to "gitea-dump-{time}.zip". Supply '-' for stdout. See type for available types.`,
|
Usage: "Name of the dump file which will be created. Supply '-' for stdout. See type for available types.",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "verbose",
|
Name: "verbose, V",
|
||||||
Aliases: []string{"V"},
|
Usage: "Show process details",
|
||||||
Usage: "Show process details",
|
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "quiet",
|
Name: "quiet, q",
|
||||||
Aliases: []string{"q"},
|
Usage: "Only display warnings and errors",
|
||||||
Usage: "Only display warnings and errors",
|
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "tempdir",
|
Name: "tempdir, t",
|
||||||
Aliases: []string{"t"},
|
Value: os.TempDir(),
|
||||||
Value: os.TempDir(),
|
Usage: "Temporary dir path",
|
||||||
Usage: "Temporary dir path",
|
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "database",
|
Name: "database, d",
|
||||||
Aliases: []string{"d"},
|
Usage: "Specify the database SQL syntax",
|
||||||
Usage: "Specify the database SQL syntax: sqlite3, mysql, mssql, postgres",
|
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "skip-repository",
|
Name: "skip-repository, R",
|
||||||
Aliases: []string{"R"},
|
Usage: "Skip the repository dumping",
|
||||||
Usage: "Skip the repository dumping",
|
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "skip-log",
|
Name: "skip-log, L",
|
||||||
Aliases: []string{"L"},
|
Usage: "Skip the log dumping",
|
||||||
Usage: "Skip the log dumping",
|
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "skip-custom-dir",
|
Name: "skip-custom-dir",
|
||||||
Usage: "Skip custom directory",
|
Usage: "Skip custom directory",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "skip-lfs-data",
|
Name: "skip-lfs-data",
|
||||||
Usage: "Skip LFS data",
|
Usage: "Skip LFS data",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "skip-attachment-data",
|
Name: "skip-attachment-data",
|
||||||
Usage: "Skip attachment data",
|
Usage: "Skip attachment data",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "skip-package-data",
|
Name: "skip-package-data",
|
||||||
Usage: "Skip package data",
|
Usage: "Skip package data",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "skip-index",
|
Name: "skip-index",
|
||||||
Usage: "Skip bleve index data",
|
Usage: "Skip bleve index data",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.GenericFlag{
|
||||||
Name: "skip-db",
|
|
||||||
Usage: "Skip database",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "type",
|
Name: "type",
|
||||||
Usage: `Dump output format, default to "zip", supported types: ` + strings.Join(dump.SupportedOutputTypes, ", "),
|
Value: outputTypeEnum,
|
||||||
|
Usage: fmt.Sprintf("Dump output format: %s", outputTypeEnum.Join()),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func fatal(format string, args ...any) {
|
func fatal(format string, args ...any) {
|
||||||
|
fmt.Fprintf(os.Stderr, format+"\n", args...)
|
||||||
log.Fatal(format, args...)
|
log.Fatal(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDump(ctx *cli.Context) error {
|
func runDump(ctx *cli.Context) error {
|
||||||
|
var file *os.File
|
||||||
|
fileName := ctx.String("file")
|
||||||
|
outType := ctx.String("type")
|
||||||
|
if fileName == "-" {
|
||||||
|
file = os.Stdout
|
||||||
|
setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr)
|
||||||
|
} else {
|
||||||
|
for _, suffix := range outputTypeEnum.Enum {
|
||||||
|
if strings.HasSuffix(fileName, "."+suffix) {
|
||||||
|
fileName = strings.TrimSuffix(fileName, "."+suffix)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fileName += "." + outType
|
||||||
|
}
|
||||||
setting.MustInstalled()
|
setting.MustInstalled()
|
||||||
|
|
||||||
quite := ctx.Bool("quiet")
|
// make sure we are logging to the console no matter what the configuration tells us do to
|
||||||
verbose := ctx.Bool("verbose")
|
// FIXME: don't use CfgProvider directly
|
||||||
if verbose && quite {
|
if _, err := setting.CfgProvider.Section("log").NewKey("MODE", "console"); err != nil {
|
||||||
fatal("Option --quiet and --verbose cannot both be set")
|
fatal("Setting logging mode to console failed: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := setting.CfgProvider.Section("log.console").NewKey("STDERR", "true"); err != nil {
|
||||||
|
fatal("Setting console logger to stderr failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// outFileName is either "-" or a file name (will be made absolute)
|
// Set loglevel to Warn if quiet-mode is requested
|
||||||
outFileName, outType := dump.PrepareFileNameAndType(ctx.String("file"), ctx.String("type"))
|
if ctx.Bool("quiet") {
|
||||||
if outType == "" {
|
if _, err := setting.CfgProvider.Section("log.console").NewKey("LEVEL", "Warn"); err != nil {
|
||||||
fatal("Invalid output type")
|
fatal("Setting console log-level failed: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
outFile := os.Stdout
|
if !setting.InstallLock {
|
||||||
if outFileName != "-" {
|
log.Error("Is '%s' really the right config path?\n", setting.CustomConf)
|
||||||
var err error
|
return fmt.Errorf("gitea is not initialized")
|
||||||
if outFileName, err = filepath.Abs(outFileName); err != nil {
|
|
||||||
fatal("Unable to get absolute path of dump file: %v", err)
|
|
||||||
}
|
|
||||||
if exist, _ := util.IsExist(outFileName); exist {
|
|
||||||
fatal("Dump file %q exists", outFileName)
|
|
||||||
}
|
|
||||||
if outFile, err = os.Create(outFileName); err != nil {
|
|
||||||
fatal("Unable to create dump file %q: %v", outFileName, err)
|
|
||||||
}
|
|
||||||
defer outFile.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setupConsoleLogger(util.Iif(quite, log.WARN, log.INFO), log.CanColorStderr, os.Stderr)
|
|
||||||
|
|
||||||
setting.DisableLoggerInit()
|
|
||||||
setting.LoadSettings() // cannot access session settings otherwise
|
setting.LoadSettings() // cannot access session settings otherwise
|
||||||
|
|
||||||
|
verbose := ctx.Bool("verbose")
|
||||||
|
if verbose && ctx.Bool("quiet") {
|
||||||
|
return fmt.Errorf("--quiet and --verbose cannot both be set")
|
||||||
|
}
|
||||||
|
|
||||||
stdCtx, cancel := installSignals()
|
stdCtx, cancel := installSignals()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
@@ -144,32 +219,44 @@ func runDump(ctx *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = storage.Init(); err != nil {
|
if err := storage.Init(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
archiverGeneric, err := archiver.ByExtension("." + outType)
|
if file == nil {
|
||||||
|
file, err = os.Create(fileName)
|
||||||
|
if err != nil {
|
||||||
|
fatal("Unable to open %s: %v", fileName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
absFileName, err := filepath.Abs(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var iface any
|
||||||
|
if fileName == "-" {
|
||||||
|
iface, err = archiver.ByExtension(fmt.Sprintf(".%s", outType))
|
||||||
|
} else {
|
||||||
|
iface, err = archiver.ByExtension(fileName)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal("Unable to get archiver for extension: %v", err)
|
fatal("Unable to get archiver for extension: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
archiverWriter := archiverGeneric.(archiver.Writer)
|
w, _ := iface.(archiver.Writer)
|
||||||
if err := archiverWriter.Create(outFile); err != nil {
|
if err := w.Create(file); err != nil {
|
||||||
fatal("Creating archiver.Writer failed: %v", err)
|
fatal("Creating archiver.Writer failed: %v", err)
|
||||||
}
|
}
|
||||||
defer archiverWriter.Close()
|
defer w.Close()
|
||||||
|
|
||||||
dumper := &dump.Dumper{
|
|
||||||
Writer: archiverWriter,
|
|
||||||
Verbose: verbose,
|
|
||||||
}
|
|
||||||
dumper.GlobalExcludeAbsPath(outFileName)
|
|
||||||
|
|
||||||
if ctx.IsSet("skip-repository") && ctx.Bool("skip-repository") {
|
if ctx.IsSet("skip-repository") && ctx.Bool("skip-repository") {
|
||||||
log.Info("Skip dumping local repositories")
|
log.Info("Skip dumping local repositories")
|
||||||
} else {
|
} else {
|
||||||
log.Info("Dumping local repositories... %s", setting.RepoRootPath)
|
log.Info("Dumping local repositories... %s", setting.RepoRootPath)
|
||||||
if err := dumper.AddRecursiveExclude("repos", setting.RepoRootPath, nil); err != nil {
|
if err := addRecursiveExclude(w, "repos", setting.RepoRootPath, []string{absFileName}, verbose); err != nil {
|
||||||
fatal("Failed to include repositories: %v", err)
|
fatal("Failed to include repositories: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,52 +269,49 @@ func runDump(ctx *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return dumper.AddReader(object, info, path.Join("data", "lfs", objPath))
|
|
||||||
|
return addReader(w, object, info, path.Join("data", "lfs", objPath), verbose)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
fatal("Failed to dump LFS objects: %v", err)
|
fatal("Failed to dump LFS objects: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.Bool("skip-db") {
|
tmpDir := ctx.String("tempdir")
|
||||||
// Ensure that we don't dump the database file that may reside in setting.AppDataPath or elsewhere.
|
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
||||||
dumper.GlobalExcludeAbsPath(setting.Database.Path)
|
fatal("Path does not exist: %s", tmpDir)
|
||||||
log.Info("Skipping database")
|
|
||||||
} else {
|
|
||||||
tmpDir := ctx.String("tempdir")
|
|
||||||
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
|
||||||
fatal("Path does not exist: %s", tmpDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
dbDump, err := os.CreateTemp(tmpDir, "gitea-db.sql")
|
|
||||||
if err != nil {
|
|
||||||
fatal("Failed to create tmp file: %v", err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = dbDump.Close()
|
|
||||||
if err := util.Remove(dbDump.Name()); err != nil {
|
|
||||||
log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
targetDBType := ctx.String("database")
|
|
||||||
if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
|
|
||||||
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
|
|
||||||
} else {
|
|
||||||
log.Info("Dumping database...")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
|
|
||||||
fatal("Failed to dump database: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = dumper.AddFile("gitea-db.sql", dbDump.Name()); err != nil {
|
|
||||||
fatal("Failed to include gitea-db.sql: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Adding custom configuration file from %s", setting.CustomConf)
|
dbDump, err := os.CreateTemp(tmpDir, "gitea-db.sql")
|
||||||
if err = dumper.AddFile("app.ini", setting.CustomConf); err != nil {
|
if err != nil {
|
||||||
fatal("Failed to include specified app.ini: %v", err)
|
fatal("Failed to create tmp file: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = dbDump.Close()
|
||||||
|
if err := util.Remove(dbDump.Name()); err != nil {
|
||||||
|
log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
targetDBType := ctx.String("database")
|
||||||
|
if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
|
||||||
|
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
|
||||||
|
} else {
|
||||||
|
log.Info("Dumping database...")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
|
||||||
|
fatal("Failed to dump database: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := addFile(w, "gitea-db.sql", dbDump.Name(), verbose); err != nil {
|
||||||
|
fatal("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 := addFile(w, "app.ini", setting.CustomConf, verbose); err != nil {
|
||||||
|
fatal("Failed to include specified app.ini: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.IsSet("skip-custom-dir") && ctx.Bool("skip-custom-dir") {
|
if ctx.IsSet("skip-custom-dir") && ctx.Bool("skip-custom-dir") {
|
||||||
@@ -235,8 +319,8 @@ func runDump(ctx *cli.Context) error {
|
|||||||
} else {
|
} else {
|
||||||
customDir, err := os.Stat(setting.CustomPath)
|
customDir, err := os.Stat(setting.CustomPath)
|
||||||
if err == nil && customDir.IsDir() {
|
if err == nil && customDir.IsDir() {
|
||||||
if is, _ := dump.IsSubdir(setting.AppDataPath, setting.CustomPath); !is {
|
if is, _ := isSubdir(setting.AppDataPath, setting.CustomPath); !is {
|
||||||
if err := dumper.AddRecursiveExclude("custom", setting.CustomPath, nil); err != nil {
|
if err := addRecursiveExclude(w, "custom", setting.CustomPath, []string{absFileName}, verbose); err != nil {
|
||||||
fatal("Failed to include custom: %v", err)
|
fatal("Failed to include custom: %v", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -273,7 +357,8 @@ func runDump(ctx *cli.Context) error {
|
|||||||
excludes = append(excludes, setting.Attachment.Storage.Path)
|
excludes = append(excludes, setting.Attachment.Storage.Path)
|
||||||
excludes = append(excludes, setting.Packages.Storage.Path)
|
excludes = append(excludes, setting.Packages.Storage.Path)
|
||||||
excludes = append(excludes, setting.Log.RootPath)
|
excludes = append(excludes, setting.Log.RootPath)
|
||||||
if err := dumper.AddRecursiveExclude("data", setting.AppDataPath, excludes); err != nil {
|
excludes = append(excludes, absFileName)
|
||||||
|
if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil {
|
||||||
fatal("Failed to include data directory: %v", err)
|
fatal("Failed to include data directory: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,7 +370,8 @@ func runDump(ctx *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return dumper.AddReader(object, info, path.Join("data", "attachments", objPath))
|
|
||||||
|
return addReader(w, object, info, path.Join("data", "attachments", objPath), verbose)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
fatal("Failed to dump attachments: %v", err)
|
fatal("Failed to dump attachments: %v", err)
|
||||||
}
|
}
|
||||||
@@ -299,7 +385,8 @@ func runDump(ctx *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return dumper.AddReader(object, info, path.Join("data", "packages", objPath))
|
|
||||||
|
return addReader(w, object, info, path.Join("data", "packages", objPath), verbose)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
fatal("Failed to dump packages: %v", err)
|
fatal("Failed to dump packages: %v", err)
|
||||||
}
|
}
|
||||||
@@ -315,23 +402,80 @@ func runDump(ctx *cli.Context) error {
|
|||||||
log.Error("Unable to check if %s exists. Error: %v", setting.Log.RootPath, err)
|
log.Error("Unable to check if %s exists. Error: %v", setting.Log.RootPath, err)
|
||||||
}
|
}
|
||||||
if isExist {
|
if isExist {
|
||||||
if err := dumper.AddRecursiveExclude("log", setting.Log.RootPath, nil); err != nil {
|
if err := addRecursiveExclude(w, "log", setting.Log.RootPath, []string{absFileName}, verbose); err != nil {
|
||||||
fatal("Failed to include log: %v", err)
|
fatal("Failed to include log: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if outFileName == "-" {
|
if fileName != "-" {
|
||||||
log.Info("Finish dumping to stdout")
|
if err = w.Close(); err != nil {
|
||||||
} else {
|
_ = util.Remove(fileName)
|
||||||
if err = archiverWriter.Close(); err != nil {
|
fatal("Failed to save %s: %v", fileName, err)
|
||||||
_ = os.Remove(outFileName)
|
|
||||||
fatal("Failed to save %q: %v", outFileName, err)
|
|
||||||
}
|
}
|
||||||
if err = os.Chmod(outFileName, 0o600); err != nil {
|
|
||||||
|
if err := os.Chmod(fileName, 0o600); err != nil {
|
||||||
log.Info("Can't change file access permissions mask to 0600: %v", err)
|
log.Info("Can't change file access permissions mask to 0600: %v", err)
|
||||||
}
|
}
|
||||||
log.Info("Finish dumping in file %s", outFileName)
|
}
|
||||||
|
|
||||||
|
if fileName != "-" {
|
||||||
|
log.Info("Finish dumping in file %s", fileName)
|
||||||
|
} else {
|
||||||
|
log.Info("Finish dumping to stdout")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// addRecursiveExclude zips absPath to specified insidePath inside writer excluding excludeAbsPath
|
||||||
|
func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeAbsPath []string, verbose bool) error {
|
||||||
|
absPath, err := filepath.Abs(absPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dir, err := os.Open(absPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dir.Close()
|
||||||
|
|
||||||
|
files, err := dir.Readdir(0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, file := range files {
|
||||||
|
currentAbsPath := path.Join(absPath, file.Name())
|
||||||
|
currentInsidePath := path.Join(insidePath, file.Name())
|
||||||
|
if file.IsDir() {
|
||||||
|
if !util.SliceContainsString(excludeAbsPath, currentAbsPath) {
|
||||||
|
if err := addFile(w, currentInsidePath, currentAbsPath, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = addRecursiveExclude(w, currentInsidePath, currentAbsPath, excludeAbsPath, verbose); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// only copy regular files and symlink regular files, skip non-regular files like socket/pipe/...
|
||||||
|
shouldAdd := file.Mode().IsRegular()
|
||||||
|
if !shouldAdd && file.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||||
|
target, err := filepath.EvalSymlinks(currentAbsPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
targetStat, err := os.Stat(target)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
shouldAdd = targetStat.Mode().IsRegular()
|
||||||
|
}
|
||||||
|
if shouldAdd {
|
||||||
|
if err = addFile(w, currentInsidePath, currentAbsPath, verbose); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,58 +19,57 @@ import (
|
|||||||
"code.gitea.io/gitea/services/convert"
|
"code.gitea.io/gitea/services/convert"
|
||||||
"code.gitea.io/gitea/services/migrations"
|
"code.gitea.io/gitea/services/migrations"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdDumpRepository represents the available dump repository sub-command.
|
// CmdDumpRepository represents the available dump repository sub-command.
|
||||||
var CmdDumpRepository = &cli.Command{
|
var CmdDumpRepository = cli.Command{
|
||||||
Name: "dump-repo",
|
Name: "dump-repo",
|
||||||
Usage: "Dump the repository from git/github/gitea/gitlab",
|
Usage: "Dump the repository from git/github/gitea/gitlab",
|
||||||
Description: "This is a command for dumping the repository data.",
|
Description: "This is a command for dumping the repository data.",
|
||||||
Action: runDumpRepository,
|
Action: runDumpRepository,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "git_service",
|
Name: "git_service",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "Git service, git, github, gitea, gitlab. If clone_addr could be recognized, this could be ignored.",
|
Usage: "Git service, git, github, gitea, gitlab. If clone_addr could be recognized, this could be ignored.",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "repo_dir",
|
Name: "repo_dir, r",
|
||||||
Aliases: []string{"r"},
|
Value: "./data",
|
||||||
Value: "./data",
|
Usage: "Repository dir path to store the data",
|
||||||
Usage: "Repository dir path to store the data",
|
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "clone_addr",
|
Name: "clone_addr",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "The URL will be clone, currently could be a git/github/gitea/gitlab http/https URL",
|
Usage: "The URL will be clone, currently could be a git/github/gitea/gitlab http/https URL",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "auth_username",
|
Name: "auth_username",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "The username to visit the clone_addr",
|
Usage: "The username to visit the clone_addr",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "auth_password",
|
Name: "auth_password",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "The password to visit the clone_addr",
|
Usage: "The password to visit the clone_addr",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "auth_token",
|
Name: "auth_token",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "The personal token to visit the clone_addr",
|
Usage: "The personal token to visit the clone_addr",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "owner_name",
|
Name: "owner_name",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "The data will be stored on a directory with owner name if not empty",
|
Usage: "The data will be stored on a directory with owner name if not empty",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "repo_name",
|
Name: "repo_name",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "The data will be stored on a directory with repository name if not empty",
|
Usage: "The data will be stored on a directory with repository name if not empty",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "units",
|
Name: "units",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: `Which items will be migrated, one or more units should be separated as comma.
|
Usage: `Which items will be migrated, one or more units should be separated as comma.
|
||||||
|
|||||||
@@ -19,74 +19,70 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"github.com/gobwas/glob"
|
"github.com/gobwas/glob"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdEmbedded represents the available extract sub-command.
|
// CmdEmbedded represents the available extract sub-command.
|
||||||
var (
|
var (
|
||||||
CmdEmbedded = &cli.Command{
|
CmdEmbedded = cli.Command{
|
||||||
Name: "embedded",
|
Name: "embedded",
|
||||||
Usage: "Extract embedded resources",
|
Usage: "Extract embedded resources",
|
||||||
Description: "A command for extracting embedded resources, like templates and images",
|
Description: "A command for extracting embedded resources, like templates and images",
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []cli.Command{
|
||||||
subcmdList,
|
subcmdList,
|
||||||
subcmdView,
|
subcmdView,
|
||||||
subcmdExtract,
|
subcmdExtract,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
subcmdList = &cli.Command{
|
subcmdList = cli.Command{
|
||||||
Name: "list",
|
Name: "list",
|
||||||
Usage: "List files matching the given pattern",
|
Usage: "List files matching the given pattern",
|
||||||
Action: runList,
|
Action: runList,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "include-vendored",
|
Name: "include-vendored,vendor",
|
||||||
Aliases: []string{"vendor"},
|
Usage: "Include files under public/vendor as well",
|
||||||
Usage: "Include files under public/vendor as well",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
subcmdView = &cli.Command{
|
subcmdView = cli.Command{
|
||||||
Name: "view",
|
Name: "view",
|
||||||
Usage: "View a file matching the given pattern",
|
Usage: "View a file matching the given pattern",
|
||||||
Action: runView,
|
Action: runView,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "include-vendored",
|
Name: "include-vendored,vendor",
|
||||||
Aliases: []string{"vendor"},
|
Usage: "Include files under public/vendor as well",
|
||||||
Usage: "Include files under public/vendor as well",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
subcmdExtract = &cli.Command{
|
subcmdExtract = cli.Command{
|
||||||
Name: "extract",
|
Name: "extract",
|
||||||
Usage: "Extract resources",
|
Usage: "Extract resources",
|
||||||
Action: runExtract,
|
Action: runExtract,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "include-vendored",
|
Name: "include-vendored,vendor",
|
||||||
Aliases: []string{"vendor"},
|
Usage: "Include files under public/vendor as well",
|
||||||
Usage: "Include files under public/vendor as well",
|
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "overwrite",
|
Name: "overwrite",
|
||||||
Usage: "Overwrite files if they already exist",
|
Usage: "Overwrite files if they already exist",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "rename",
|
Name: "rename",
|
||||||
Usage: "Rename files as {name}.bak if they already exist (overwrites previous .bak)",
|
Usage: "Rename files as {name}.bak if they already exist (overwrites previous .bak)",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "custom",
|
Name: "custom",
|
||||||
Usage: "Extract to the 'custom' directory as per app.ini",
|
Usage: "Extract to the 'custom' directory as per app.ini",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "destination",
|
Name: "destination,dest-dir",
|
||||||
Aliases: []string{"dest-dir"},
|
Usage: "Extract to the specified directory",
|
||||||
Usage: "Extract to the specified directory",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -103,7 +99,7 @@ type assetFile struct {
|
|||||||
func initEmbeddedExtractor(c *cli.Context) error {
|
func initEmbeddedExtractor(c *cli.Context) error {
|
||||||
setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr)
|
setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr)
|
||||||
|
|
||||||
patterns, err := compileCollectPatterns(c.Args().Slice())
|
patterns, err := compileCollectPatterns(c.Args())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -157,9 +153,9 @@ func runViewDo(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(matchedAssetFiles) == 0 {
|
if len(matchedAssetFiles) == 0 {
|
||||||
return errors.New("no files matched the given pattern")
|
return fmt.Errorf("no files matched the given pattern")
|
||||||
} else if len(matchedAssetFiles) > 1 {
|
} else if len(matchedAssetFiles) > 1 {
|
||||||
return errors.New("too many files matched the given pattern, try to be more specific")
|
return fmt.Errorf("too many files matched the given pattern, try to be more specific")
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := matchedAssetFiles[0].fs.ReadFile(matchedAssetFiles[0].name)
|
data, err := matchedAssetFiles[0].fs.ReadFile(matchedAssetFiles[0].name)
|
||||||
@@ -179,8 +175,8 @@ func runExtractDo(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.NArg() == 0 {
|
if len(c.Args()) == 0 {
|
||||||
return errors.New("a list of pattern of files to extract is mandatory (e.g. '**' for all)")
|
return fmt.Errorf("a list of pattern of files to extract is mandatory (e.g. '**' for all)")
|
||||||
}
|
}
|
||||||
|
|
||||||
destdir := "."
|
destdir := "."
|
||||||
|
|||||||
@@ -11,43 +11,43 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/generate"
|
"code.gitea.io/gitea/modules/generate"
|
||||||
|
|
||||||
"github.com/mattn/go-isatty"
|
"github.com/mattn/go-isatty"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// CmdGenerate represents the available generate sub-command.
|
// CmdGenerate represents the available generate sub-command.
|
||||||
CmdGenerate = &cli.Command{
|
CmdGenerate = cli.Command{
|
||||||
Name: "generate",
|
Name: "generate",
|
||||||
Usage: "Generate Gitea's secrets/keys/tokens",
|
Usage: "Command line interface for running generators",
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []cli.Command{
|
||||||
subcmdSecret,
|
subcmdSecret,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
subcmdSecret = &cli.Command{
|
subcmdSecret = cli.Command{
|
||||||
Name: "secret",
|
Name: "secret",
|
||||||
Usage: "Generate a secret token",
|
Usage: "Generate a secret token",
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []cli.Command{
|
||||||
microcmdGenerateInternalToken,
|
microcmdGenerateInternalToken,
|
||||||
microcmdGenerateLfsJwtSecret,
|
microcmdGenerateLfsJwtSecret,
|
||||||
microcmdGenerateSecretKey,
|
microcmdGenerateSecretKey,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
microcmdGenerateInternalToken = &cli.Command{
|
microcmdGenerateInternalToken = cli.Command{
|
||||||
Name: "INTERNAL_TOKEN",
|
Name: "INTERNAL_TOKEN",
|
||||||
Usage: "Generate a new INTERNAL_TOKEN",
|
Usage: "Generate a new INTERNAL_TOKEN",
|
||||||
Action: runGenerateInternalToken,
|
Action: runGenerateInternalToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
microcmdGenerateLfsJwtSecret = &cli.Command{
|
microcmdGenerateLfsJwtSecret = cli.Command{
|
||||||
Name: "JWT_SECRET",
|
Name: "JWT_SECRET",
|
||||||
Aliases: []string{"LFS_JWT_SECRET"},
|
Aliases: []string{"LFS_JWT_SECRET"},
|
||||||
Usage: "Generate a new JWT_SECRET",
|
Usage: "Generate a new JWT_SECRET",
|
||||||
Action: runGenerateLfsJwtSecret,
|
Action: runGenerateLfsJwtSecret,
|
||||||
}
|
}
|
||||||
|
|
||||||
microcmdGenerateSecretKey = &cli.Command{
|
microcmdGenerateSecretKey = cli.Command{
|
||||||
Name: "SECRET_KEY",
|
Name: "SECRET_KEY",
|
||||||
Usage: "Generate a new SECRET_KEY",
|
Usage: "Generate a new SECRET_KEY",
|
||||||
Action: runGenerateSecretKey,
|
Action: runGenerateSecretKey,
|
||||||
@@ -70,12 +70,12 @@ func runGenerateInternalToken(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runGenerateLfsJwtSecret(c *cli.Context) error {
|
func runGenerateLfsJwtSecret(c *cli.Context) error {
|
||||||
_, jwtSecretBase64, err := generate.NewJwtSecretWithBase64()
|
JWTSecretBase64, err := generate.NewJwtSecretBase64()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s", jwtSecretBase64)
|
fmt.Printf("%s", JWTSecretBase64)
|
||||||
|
|
||||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
|
|||||||
111
cmd/hook.go
111
cmd/hook.go
@@ -20,7 +20,7 @@ import (
|
|||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -29,12 +29,12 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// CmdHook represents the available hooks sub-command.
|
// CmdHook represents the available hooks sub-command.
|
||||||
CmdHook = &cli.Command{
|
CmdHook = cli.Command{
|
||||||
Name: "hook",
|
Name: "hook",
|
||||||
Usage: "(internal) Should only be called by Git",
|
Usage: "Delegate commands to corresponding Git hooks",
|
||||||
Description: "Delegate commands to corresponding Git hooks",
|
Description: "This should only be called by Git",
|
||||||
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []cli.Command{
|
||||||
subcmdHookPreReceive,
|
subcmdHookPreReceive,
|
||||||
subcmdHookUpdate,
|
subcmdHookUpdate,
|
||||||
subcmdHookPostReceive,
|
subcmdHookPostReceive,
|
||||||
@@ -42,47 +42,47 @@ var (
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
subcmdHookPreReceive = &cli.Command{
|
subcmdHookPreReceive = cli.Command{
|
||||||
Name: "pre-receive",
|
Name: "pre-receive",
|
||||||
Usage: "Delegate pre-receive Git hook",
|
Usage: "Delegate pre-receive Git hook",
|
||||||
Description: "This command should only be called by Git",
|
Description: "This command should only be called by Git",
|
||||||
Action: runHookPreReceive,
|
Action: runHookPreReceive,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
subcmdHookUpdate = &cli.Command{
|
subcmdHookUpdate = cli.Command{
|
||||||
Name: "update",
|
Name: "update",
|
||||||
Usage: "Delegate update Git hook",
|
Usage: "Delegate update Git hook",
|
||||||
Description: "This command should only be called by Git",
|
Description: "This command should only be called by Git",
|
||||||
Action: runHookUpdate,
|
Action: runHookUpdate,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
subcmdHookPostReceive = &cli.Command{
|
subcmdHookPostReceive = cli.Command{
|
||||||
Name: "post-receive",
|
Name: "post-receive",
|
||||||
Usage: "Delegate post-receive Git hook",
|
Usage: "Delegate post-receive Git hook",
|
||||||
Description: "This command should only be called by Git",
|
Description: "This command should only be called by Git",
|
||||||
Action: runHookPostReceive,
|
Action: runHookPostReceive,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// Note: new hook since git 2.29
|
// Note: new hook since git 2.29
|
||||||
subcmdHookProcReceive = &cli.Command{
|
subcmdHookProcReceive = cli.Command{
|
||||||
Name: "proc-receive",
|
Name: "proc-receive",
|
||||||
Usage: "Delegate proc-receive Git hook",
|
Usage: "Delegate proc-receive Git hook",
|
||||||
Description: "This command should only be called by Git",
|
Description: "This command should only be called by Git",
|
||||||
Action: runHookProcReceive,
|
Action: runHookProcReceive,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -220,7 +220,10 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
supportProcReceive := git.DefaultFeatures().SupportProcReceive
|
supportProcReceive := false
|
||||||
|
if git.CheckGitVersionAtLeast("2.29") == nil {
|
||||||
|
supportProcReceive = true
|
||||||
|
}
|
||||||
|
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
// TODO: support news feeds for wiki
|
// TODO: support news feeds for wiki
|
||||||
@@ -290,22 +293,8 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// runHookUpdate avoid to do heavy operations on update hook because it will be
|
|
||||||
// invoked for every ref update which does not like pre-receive and post-receive
|
|
||||||
func runHookUpdate(c *cli.Context) error {
|
func runHookUpdate(c *cli.Context) error {
|
||||||
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update is empty and is kept only for backwards compatibility
|
// Update is empty and is kept only for backwards compatibility
|
||||||
if len(os.Args) < 3 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
refName := git.RefName(os.Args[len(os.Args)-3])
|
|
||||||
if refName.IsPull() {
|
|
||||||
// ignore update to refs/pull/xxx/head, so we don't need to output any information
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,7 +305,7 @@ func runHookPostReceive(c *cli.Context) error {
|
|||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, c.Bool("debug"))
|
||||||
|
|
||||||
// First of all run update-server-info no matter what
|
// First of all run update-server-info no matter what
|
||||||
if _, _, err := git.NewCommand("update-server-info").RunStdString(ctx, nil); err != nil {
|
if _, _, err := git.NewCommand(ctx, "update-server-info").RunStdString(nil); err != nil {
|
||||||
return fmt.Errorf("Failed to call 'git update-server-info': %w", err)
|
return fmt.Errorf("Failed to call 'git update-server-info': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,7 +341,6 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
isWiki, _ := strconv.ParseBool(os.Getenv(repo_module.EnvRepoIsWiki))
|
isWiki, _ := strconv.ParseBool(os.Getenv(repo_module.EnvRepoIsWiki))
|
||||||
repoName := os.Getenv(repo_module.EnvRepoName)
|
repoName := os.Getenv(repo_module.EnvRepoName)
|
||||||
pusherID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
|
pusherID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
|
||||||
prID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPRID), 10, 64)
|
|
||||||
pusherName := os.Getenv(repo_module.EnvPusherName)
|
pusherName := os.Getenv(repo_module.EnvPusherName)
|
||||||
|
|
||||||
hookOptions := private.HookOptions{
|
hookOptions := private.HookOptions{
|
||||||
@@ -362,8 +350,6 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
||||||
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
||||||
GitPushOptions: pushOptions(),
|
GitPushOptions: pushOptions(),
|
||||||
PullRequestID: prID,
|
|
||||||
PushTrigger: repo_module.PushTrigger(os.Getenv(repo_module.EnvPushTrigger)),
|
|
||||||
}
|
}
|
||||||
oldCommitIDs := make([]string, hookBatchSize)
|
oldCommitIDs := make([]string, hookBatchSize)
|
||||||
newCommitIDs := make([]string, hookBatchSize)
|
newCommitIDs := make([]string, hookBatchSize)
|
||||||
@@ -390,9 +376,7 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
oldCommitIDs[count] = string(fields[0])
|
oldCommitIDs[count] = string(fields[0])
|
||||||
newCommitIDs[count] = string(fields[1])
|
newCommitIDs[count] = string(fields[1])
|
||||||
refFullNames[count] = git.RefName(fields[2])
|
refFullNames[count] = git.RefName(fields[2])
|
||||||
|
if refFullNames[count] == git.BranchPrefix+"master" && newCommitIDs[count] != git.EmptySHA && count == total {
|
||||||
commitID, _ := git.NewIDFromString(newCommitIDs[count])
|
|
||||||
if refFullNames[count] == git.BranchPrefix+"master" && !commitID.IsZero() && count == total {
|
|
||||||
masterPushed = true
|
masterPushed = true
|
||||||
}
|
}
|
||||||
count++
|
count++
|
||||||
@@ -462,24 +446,21 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
|
|
||||||
func hookPrintResults(results []private.HookPostReceiveBranchResult) {
|
func hookPrintResults(results []private.HookPostReceiveBranchResult) {
|
||||||
for _, res := range results {
|
for _, res := range results {
|
||||||
hookPrintResult(res.Message, res.Create, res.Branch, res.URL)
|
if !res.Message {
|
||||||
}
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
func hookPrintResult(output, isCreate bool, branch, url string) {
|
fmt.Fprintln(os.Stderr, "")
|
||||||
if !output {
|
if res.Create {
|
||||||
return
|
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()
|
||||||
}
|
}
|
||||||
fmt.Fprintln(os.Stderr, "")
|
|
||||||
if isCreate {
|
|
||||||
fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", branch)
|
|
||||||
fmt.Fprintf(os.Stderr, " %s\n", url)
|
|
||||||
} else {
|
|
||||||
fmt.Fprint(os.Stderr, "Visit the existing pull request:\n")
|
|
||||||
fmt.Fprintf(os.Stderr, " %s\n", url)
|
|
||||||
}
|
|
||||||
fmt.Fprintln(os.Stderr, "")
|
|
||||||
_ = os.Stderr.Sync()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func pushOptions() map[string]string {
|
func pushOptions() map[string]string {
|
||||||
@@ -511,7 +492,7 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !git.DefaultFeatures().SupportProcReceive {
|
if git.CheckGitVersionAtLeast("2.29") != nil {
|
||||||
return fail(ctx, "No proc-receive support", "current git version doesn't support proc-receive.")
|
return fail(ctx, "No proc-receive support", "current git version doesn't support proc-receive.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -542,14 +523,14 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
|
|
||||||
index := bytes.IndexByte(rs.Data, byte(0))
|
index := bytes.IndexByte(rs.Data, byte(0))
|
||||||
if index >= len(rs.Data) {
|
if index >= len(rs.Data) {
|
||||||
return fail(ctx, "Protocol: format error", "pkt-line: format error %s", rs.Data)
|
return fail(ctx, "Protocol: format error", "pkt-line: format error "+fmt.Sprint(rs.Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
if len(rs.Data) == 10 && rs.Data[9] == '\n' {
|
if len(rs.Data) == 10 && rs.Data[9] == '\n' {
|
||||||
index = 9
|
index = 9
|
||||||
} else {
|
} else {
|
||||||
return fail(ctx, "Protocol: format error", "pkt-line: format error %s", rs.Data)
|
return fail(ctx, "Protocol: format error", "pkt-line: format error "+fmt.Sprint(rs.Data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -591,9 +572,8 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
// S: ... ...
|
// S: ... ...
|
||||||
// S: flush-pkt
|
// S: flush-pkt
|
||||||
hookOptions := private.HookOptions{
|
hookOptions := private.HookOptions{
|
||||||
UserName: pusherName,
|
UserName: pusherName,
|
||||||
UserID: pusherID,
|
UserID: pusherID,
|
||||||
GitPushOptions: make(map[string]string),
|
|
||||||
}
|
}
|
||||||
hookOptions.OldCommitIDs = make([]string, 0, hookBatchSize)
|
hookOptions.OldCommitIDs = make([]string, 0, hookBatchSize)
|
||||||
hookOptions.NewCommitIDs = make([]string, 0, hookBatchSize)
|
hookOptions.NewCommitIDs = make([]string, 0, hookBatchSize)
|
||||||
@@ -618,6 +598,8 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
hookOptions.RefFullNames = append(hookOptions.RefFullNames, git.RefName(t[2]))
|
hookOptions.RefFullNames = append(hookOptions.RefFullNames, git.RefName(t[2]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hookOptions.GitPushOptions = make(map[string]string)
|
||||||
|
|
||||||
if hasPushOptions {
|
if hasPushOptions {
|
||||||
for {
|
for {
|
||||||
rs, err = readPktLine(ctx, reader, pktLineTypeUnknow)
|
rs, err = readPktLine(ctx, reader, pktLineTypeUnknow)
|
||||||
@@ -628,7 +610,11 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
if rs.Type == pktLineTypeFlush {
|
if rs.Type == pktLineTypeFlush {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
hookOptions.GitPushOptions.AddFromKeyValue(string(rs.Data))
|
|
||||||
|
kv := strings.SplitN(string(rs.Data), "=", 2)
|
||||||
|
if len(kv) == 2 {
|
||||||
|
hookOptions.GitPushOptions[kv[0]] = kv[1]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -683,8 +669,7 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
commitID, _ := git.NewIDFromString(rs.OldOID)
|
if rs.OldOID != git.EmptySHA {
|
||||||
if !commitID.IsZero() {
|
|
||||||
err = writeDataPktLine(ctx, os.Stdout, []byte("option old-oid "+rs.OldOID))
|
err = writeDataPktLine(ctx, os.Stdout, []byte("option old-oid "+rs.OldOID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -703,12 +688,6 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
}
|
}
|
||||||
err = writeFlushPktLine(ctx, os.Stdout)
|
err = writeFlushPktLine(ctx, os.Stdout)
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
for _, res := range resp.Results {
|
|
||||||
hookPrintResult(res.ShouldShowMessage, res.IsCreatePR, res.HeadBranch, res.URL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@ import (
|
|||||||
|
|
||||||
func TestPktLine(t *testing.T) {
|
func TestPktLine(t *testing.T) {
|
||||||
// test read
|
// test read
|
||||||
ctx := t.Context()
|
ctx := context.Background()
|
||||||
s := strings.NewReader("0000")
|
s := strings.NewReader("0000")
|
||||||
r := bufio.NewReader(s)
|
r := bufio.NewReader(s)
|
||||||
result, err := readPktLine(ctx, r, pktLineTypeFlush)
|
result, err := readPktLine(ctx, r, pktLineTypeFlush)
|
||||||
|
|||||||
53
cmd/keys.go
53
cmd/keys.go
@@ -11,40 +11,35 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdKeys represents the available keys sub-command
|
// CmdKeys represents the available keys sub-command
|
||||||
var CmdKeys = &cli.Command{
|
var CmdKeys = cli.Command{
|
||||||
Name: "keys",
|
Name: "keys",
|
||||||
Usage: "(internal) Should only be called by SSH server",
|
Usage: "This command queries the Gitea database to get the authorized command for a given ssh key fingerprint",
|
||||||
Description: "Queries the Gitea database to get the authorized command for a given ssh key fingerprint",
|
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
||||||
Before: PrepareConsoleLoggerLevel(log.FATAL),
|
Action: runKeys,
|
||||||
Action: runKeys,
|
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "expected",
|
Name: "expected, e",
|
||||||
Aliases: []string{"e"},
|
Value: "git",
|
||||||
Value: "git",
|
Usage: "Expected user for whom provide key commands",
|
||||||
Usage: "Expected user for whom provide key commands",
|
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "username",
|
Name: "username, u",
|
||||||
Aliases: []string{"u"},
|
Value: "",
|
||||||
Value: "",
|
Usage: "Username trying to log in by SSH",
|
||||||
Usage: "Username trying to log in by SSH",
|
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "type",
|
Name: "type, t",
|
||||||
Aliases: []string{"t"},
|
Value: "",
|
||||||
Value: "",
|
Usage: "Type of the SSH key provided to the SSH Server (requires content to be provided too)",
|
||||||
Usage: "Type of the SSH key provided to the SSH Server (requires content to be provided too)",
|
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "content",
|
Name: "content, k",
|
||||||
Aliases: []string{"k"},
|
Value: "",
|
||||||
Value: "",
|
Usage: "Base64 encoded content of the SSH key provided to the SSH Server (requires type to be provided too)",
|
||||||
Usage: "Base64 encoded content of the SSH key provided to the SSH Server (requires type to be provided too)",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -71,13 +66,13 @@ func runKeys(c *cli.Context) error {
|
|||||||
ctx, cancel := installSignals()
|
ctx, cancel := installSignals()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
setup(ctx, c.Bool("debug"))
|
setup(ctx, false)
|
||||||
|
|
||||||
authorizedString, extra := private.AuthorizedPublicKeyByContent(ctx, content)
|
authorizedString, extra := private.AuthorizedPublicKeyByContent(ctx, content)
|
||||||
// do not use handleCliResponseExtra or cli.NewExitError, if it exists immediately, it breaks some tests like Test_CmdKeys
|
// do not use handleCliResponseExtra or cli.NewExitError, if it exists immediately, it breaks some tests like Test_CmdKeys
|
||||||
if extra.Error != nil {
|
if extra.Error != nil {
|
||||||
return extra.Error
|
return extra.Error
|
||||||
}
|
}
|
||||||
_, _ = fmt.Fprintln(c.App.Writer, strings.TrimSpace(authorizedString.Text))
|
fmt.Println(strings.TrimSpace(authorizedString))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
func runSendMail(c *cli.Context) error {
|
func runSendMail(c *cli.Context) error {
|
||||||
@@ -45,6 +45,6 @@ func runSendMail(c *cli.Context) error {
|
|||||||
if extra.HasError() {
|
if extra.HasError() {
|
||||||
return handleCliResponseExtra(extra)
|
return handleCliResponseExtra(extra)
|
||||||
}
|
}
|
||||||
_, _ = fmt.Printf("Sent %s email(s) to all users\n", respText.Text)
|
_, _ = fmt.Printf("Sent %s email(s) to all users\n", respText)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
185
cmd/main.go
185
cmd/main.go
@@ -1,185 +0,0 @@
|
|||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// cmdHelp is our own help subcommand with more information
|
|
||||||
// Keep in mind that the "./gitea help"(subcommand) is different from "./gitea --help"(flag), the flag doesn't parse the config or output "DEFAULT CONFIGURATION:" information
|
|
||||||
func cmdHelp() *cli.Command {
|
|
||||||
c := &cli.Command{
|
|
||||||
Name: "help",
|
|
||||||
Aliases: []string{"h"},
|
|
||||||
Usage: "Shows a list of commands or help for one command",
|
|
||||||
ArgsUsage: "[command]",
|
|
||||||
Action: func(c *cli.Context) (err error) {
|
|
||||||
lineage := c.Lineage() // The order is from child to parent: help, doctor, Gitea, {Command:nil}
|
|
||||||
targetCmdIdx := 0
|
|
||||||
if c.Command.Name == "help" {
|
|
||||||
targetCmdIdx = 1
|
|
||||||
}
|
|
||||||
if lineage[targetCmdIdx+1].Command != nil {
|
|
||||||
err = cli.ShowCommandHelp(lineage[targetCmdIdx+1], lineage[targetCmdIdx].Command.Name)
|
|
||||||
} else {
|
|
||||||
err = cli.ShowAppHelp(c)
|
|
||||||
}
|
|
||||||
_, _ = fmt.Fprintf(c.App.Writer, `
|
|
||||||
DEFAULT CONFIGURATION:
|
|
||||||
AppPath: %s
|
|
||||||
WorkPath: %s
|
|
||||||
CustomPath: %s
|
|
||||||
ConfigFile: %s
|
|
||||||
|
|
||||||
`, setting.AppPath, setting.AppWorkPath, setting.CustomPath, setting.CustomConf)
|
|
||||||
return err
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func appGlobalFlags() []cli.Flag {
|
|
||||||
return []cli.Flag{
|
|
||||||
// make the builtin flags at the top
|
|
||||||
cli.HelpFlag,
|
|
||||||
|
|
||||||
// shared configuration flags, they are for global and for each sub-command at the same time
|
|
||||||
// eg: such command is valid: "./gitea --config /tmp/app.ini web --config /tmp/app.ini", while it's discouraged indeed
|
|
||||||
// keep in mind that the short flags like "-C", "-c" and "-w" are globally polluted, they can't be used for sub-commands anymore.
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "custom-path",
|
|
||||||
Aliases: []string{"C"},
|
|
||||||
Usage: "Set custom path (defaults to '{WorkPath}/custom')",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "config",
|
|
||||||
Aliases: []string{"c"},
|
|
||||||
Value: setting.CustomConf,
|
|
||||||
Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "work-path",
|
|
||||||
Aliases: []string{"w"},
|
|
||||||
Usage: "Set Gitea's working path (defaults to the Gitea's binary directory)",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func prepareSubcommandWithConfig(command *cli.Command, globalFlags []cli.Flag) {
|
|
||||||
command.Flags = append(append([]cli.Flag{}, globalFlags...), command.Flags...)
|
|
||||||
command.Action = prepareWorkPathAndCustomConf(command.Action)
|
|
||||||
command.HideHelp = true
|
|
||||||
if command.Name != "help" {
|
|
||||||
command.Subcommands = append(command.Subcommands, cmdHelp())
|
|
||||||
}
|
|
||||||
for i := range command.Subcommands {
|
|
||||||
prepareSubcommandWithConfig(command.Subcommands[i], globalFlags)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepareWorkPathAndCustomConf wraps the Action to prepare the work path and custom config
|
|
||||||
// It can't use "Before", because each level's sub-command's Before will be called one by one, so the "init" would be done multiple times
|
|
||||||
func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context) error {
|
|
||||||
return func(ctx *cli.Context) error {
|
|
||||||
var args setting.ArgWorkPathAndCustomConf
|
|
||||||
// from children to parent, check the global flags
|
|
||||||
for _, curCtx := range ctx.Lineage() {
|
|
||||||
if curCtx.IsSet("work-path") && args.WorkPath == "" {
|
|
||||||
args.WorkPath = curCtx.String("work-path")
|
|
||||||
}
|
|
||||||
if curCtx.IsSet("custom-path") && args.CustomPath == "" {
|
|
||||||
args.CustomPath = curCtx.String("custom-path")
|
|
||||||
}
|
|
||||||
if curCtx.IsSet("config") && args.CustomConf == "" {
|
|
||||||
args.CustomConf = curCtx.String("config")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
|
|
||||||
if ctx.Bool("help") || action == nil {
|
|
||||||
// the default behavior of "urfave/cli": "nil action" means "show help"
|
|
||||||
return cmdHelp().Action(ctx)
|
|
||||||
}
|
|
||||||
return action(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type AppVersion struct {
|
|
||||||
Version string
|
|
||||||
Extra string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMainApp(appVer AppVersion) *cli.App {
|
|
||||||
app := cli.NewApp()
|
|
||||||
app.Name = "Gitea"
|
|
||||||
app.HelpName = "gitea"
|
|
||||||
app.Usage = "A painless self-hosted Git service"
|
|
||||||
app.Description = `Gitea program contains "web" and other subcommands. If no subcommand is given, it starts the web server by default. Use "web" subcommand for more web server arguments, use other subcommands for other purposes.`
|
|
||||||
app.Version = appVer.Version + appVer.Extra
|
|
||||||
app.EnableBashCompletion = true
|
|
||||||
|
|
||||||
// these sub-commands need to use config file
|
|
||||||
subCmdWithConfig := []*cli.Command{
|
|
||||||
cmdHelp(), // the "help" sub-command was used to show the more information for "work path" and "custom config"
|
|
||||||
CmdWeb,
|
|
||||||
CmdServ,
|
|
||||||
CmdHook,
|
|
||||||
CmdKeys,
|
|
||||||
CmdDump,
|
|
||||||
CmdAdmin,
|
|
||||||
CmdMigrate,
|
|
||||||
CmdDoctor,
|
|
||||||
CmdManager,
|
|
||||||
CmdEmbedded,
|
|
||||||
CmdMigrateStorage,
|
|
||||||
CmdDumpRepository,
|
|
||||||
CmdRestoreRepository,
|
|
||||||
CmdActions,
|
|
||||||
}
|
|
||||||
|
|
||||||
// these sub-commands do not need the config file, and they do not depend on any path or environment variable.
|
|
||||||
subCmdStandalone := []*cli.Command{
|
|
||||||
CmdCert,
|
|
||||||
CmdGenerate,
|
|
||||||
CmdDocs,
|
|
||||||
}
|
|
||||||
|
|
||||||
app.DefaultCommand = CmdWeb.Name
|
|
||||||
|
|
||||||
globalFlags := appGlobalFlags()
|
|
||||||
app.Flags = append(app.Flags, cli.VersionFlag)
|
|
||||||
app.Flags = append(app.Flags, globalFlags...)
|
|
||||||
app.HideHelp = true // use our own help action to show helps (with more information like default config)
|
|
||||||
app.Before = PrepareConsoleLoggerLevel(log.INFO)
|
|
||||||
for i := range subCmdWithConfig {
|
|
||||||
prepareSubcommandWithConfig(subCmdWithConfig[i], globalFlags)
|
|
||||||
}
|
|
||||||
app.Commands = append(app.Commands, subCmdWithConfig...)
|
|
||||||
app.Commands = append(app.Commands, subCmdStandalone...)
|
|
||||||
|
|
||||||
setting.InitGiteaEnvVars()
|
|
||||||
return app
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunMainApp(app *cli.App, args ...string) error {
|
|
||||||
err := app.Run(args)
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(err.Error(), "flag provided but not defined:") {
|
|
||||||
// the cli package should already have output the error message, so just exit
|
|
||||||
cli.OsExiter(1)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, _ = fmt.Fprintf(app.ErrWriter, "Command error: %v\n", err)
|
|
||||||
cli.OsExiter(1)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
146
cmd/main_test.go
146
cmd/main_test.go
@@ -4,155 +4,13 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
"code.gitea.io/gitea/modules/test"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
unittest.MainTest(m)
|
unittest.MainTest(m, &unittest.TestOptions{
|
||||||
}
|
GiteaRootPath: "..",
|
||||||
|
|
||||||
func makePathOutput(workPath, customPath, customConf string) string {
|
|
||||||
return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTestApp(testCmdAction func(ctx *cli.Context) error) *cli.App {
|
|
||||||
app := NewMainApp(AppVersion{})
|
|
||||||
testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction}
|
|
||||||
prepareSubcommandWithConfig(testCmd, appGlobalFlags())
|
|
||||||
app.Commands = append(app.Commands, testCmd)
|
|
||||||
app.DefaultCommand = testCmd.Name
|
|
||||||
return app
|
|
||||||
}
|
|
||||||
|
|
||||||
type runResult struct {
|
|
||||||
Stdout string
|
|
||||||
Stderr string
|
|
||||||
ExitCode int
|
|
||||||
}
|
|
||||||
|
|
||||||
func runTestApp(app *cli.App, args ...string) (runResult, error) {
|
|
||||||
outBuf := new(strings.Builder)
|
|
||||||
errBuf := new(strings.Builder)
|
|
||||||
app.Writer = outBuf
|
|
||||||
app.ErrWriter = errBuf
|
|
||||||
exitCode := -1
|
|
||||||
defer test.MockVariableValue(&cli.ErrWriter, app.ErrWriter)()
|
|
||||||
defer test.MockVariableValue(&cli.OsExiter, func(code int) {
|
|
||||||
if exitCode == -1 {
|
|
||||||
exitCode = code // save the exit code once and then reset the writer (to simulate the exit)
|
|
||||||
app.Writer, app.ErrWriter, cli.ErrWriter = io.Discard, io.Discard, io.Discard
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
err := RunMainApp(app, args...)
|
|
||||||
return runResult{outBuf.String(), errBuf.String(), exitCode}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCliCmd(t *testing.T) {
|
|
||||||
defaultWorkPath := filepath.Dir(setting.AppPath)
|
|
||||||
defaultCustomPath := filepath.Join(defaultWorkPath, "custom")
|
|
||||||
defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini")
|
|
||||||
|
|
||||||
cli.CommandHelpTemplate = "(command help template)"
|
|
||||||
cli.AppHelpTemplate = "(app help template)"
|
|
||||||
cli.SubcommandHelpTemplate = "(subcommand help template)"
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
env map[string]string
|
|
||||||
cmd string
|
|
||||||
exp string
|
|
||||||
}{
|
|
||||||
// main command help
|
|
||||||
{
|
|
||||||
cmd: "./gitea help",
|
|
||||||
exp: "DEFAULT CONFIGURATION:",
|
|
||||||
},
|
|
||||||
|
|
||||||
// parse paths
|
|
||||||
{
|
|
||||||
cmd: "./gitea test-cmd",
|
|
||||||
exp: makePathOutput(defaultWorkPath, defaultCustomPath, defaultCustomConf),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
cmd: "./gitea -c /tmp/app.ini test-cmd",
|
|
||||||
exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
cmd: "./gitea test-cmd -c /tmp/app.ini",
|
|
||||||
exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
|
|
||||||
cmd: "./gitea test-cmd",
|
|
||||||
exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/custom/conf/app.ini"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
|
|
||||||
cmd: "./gitea test-cmd --work-path /tmp/other",
|
|
||||||
exp: makePathOutput("/tmp/other", "/tmp/other/custom", "/tmp/other/custom/conf/app.ini"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
|
|
||||||
cmd: "./gitea test-cmd --config /tmp/app-other.ini",
|
|
||||||
exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/app-other.ini"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
app := newTestApp(func(ctx *cli.Context) error {
|
|
||||||
_, _ = fmt.Fprint(ctx.App.Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
for _, c := range cases {
|
|
||||||
t.Run(c.cmd, func(t *testing.T) {
|
|
||||||
for k, v := range c.env {
|
|
||||||
t.Setenv(k, v)
|
|
||||||
}
|
|
||||||
args := strings.Split(c.cmd, " ") // for test only, "split" is good enough
|
|
||||||
r, err := runTestApp(app, args...)
|
|
||||||
assert.NoError(t, err, c.cmd)
|
|
||||||
assert.NotEmpty(t, c.exp, c.cmd)
|
|
||||||
assert.Contains(t, r.Stdout, c.exp, c.cmd)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCliCmdError(t *testing.T) {
|
|
||||||
app := newTestApp(func(ctx *cli.Context) error { return errors.New("normal error") })
|
|
||||||
r, err := runTestApp(app, "./gitea", "test-cmd")
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Equal(t, 1, r.ExitCode)
|
|
||||||
assert.Empty(t, r.Stdout)
|
|
||||||
assert.Equal(t, "Command error: normal error\n", r.Stderr)
|
|
||||||
|
|
||||||
app = newTestApp(func(ctx *cli.Context) error { return cli.Exit("exit error", 2) })
|
|
||||||
r, err = runTestApp(app, "./gitea", "test-cmd")
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Equal(t, 2, r.ExitCode)
|
|
||||||
assert.Empty(t, r.Stdout)
|
|
||||||
assert.Equal(t, "exit error\n", r.Stderr)
|
|
||||||
|
|
||||||
app = newTestApp(func(ctx *cli.Context) error { return nil })
|
|
||||||
r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such")
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.Equal(t, 1, r.ExitCode)
|
|
||||||
assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stdout)
|
|
||||||
assert.Empty(t, r.Stderr) // the cli package's strange behavior, the error message is not in stderr ....
|
|
||||||
|
|
||||||
app = newTestApp(func(ctx *cli.Context) error { return nil })
|
|
||||||
r, err = runTestApp(app, "./gitea", "test-cmd")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called
|
|
||||||
assert.Empty(t, r.Stdout)
|
|
||||||
assert.Empty(t, r.Stderr)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,16 +9,16 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// CmdManager represents the manager command
|
// CmdManager represents the manager command
|
||||||
CmdManager = &cli.Command{
|
CmdManager = cli.Command{
|
||||||
Name: "manager",
|
Name: "manager",
|
||||||
Usage: "Manage the running gitea process",
|
Usage: "Manage the running gitea process",
|
||||||
Description: "This is a command for managing the running gitea process",
|
Description: "This is a command for managing the running gitea process",
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []cli.Command{
|
||||||
subcmdShutdown,
|
subcmdShutdown,
|
||||||
subcmdRestart,
|
subcmdRestart,
|
||||||
subcmdReloadTemplates,
|
subcmdReloadTemplates,
|
||||||
@@ -27,80 +27,80 @@ var (
|
|||||||
subCmdProcesses,
|
subCmdProcesses,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
subcmdShutdown = &cli.Command{
|
subcmdShutdown = cli.Command{
|
||||||
Name: "shutdown",
|
Name: "shutdown",
|
||||||
Usage: "Gracefully shutdown the running process",
|
Usage: "Gracefully shutdown the running process",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: runShutdown,
|
Action: runShutdown,
|
||||||
}
|
}
|
||||||
subcmdRestart = &cli.Command{
|
subcmdRestart = cli.Command{
|
||||||
Name: "restart",
|
Name: "restart",
|
||||||
Usage: "Gracefully restart the running process - (not implemented for windows servers)",
|
Usage: "Gracefully restart the running process - (not implemented for windows servers)",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: runRestart,
|
Action: runRestart,
|
||||||
}
|
}
|
||||||
subcmdReloadTemplates = &cli.Command{
|
subcmdReloadTemplates = cli.Command{
|
||||||
Name: "reload-templates",
|
Name: "reload-templates",
|
||||||
Usage: "Reload template files in the running process",
|
Usage: "Reload template files in the running process",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action: runReloadTemplates,
|
Action: runReloadTemplates,
|
||||||
}
|
}
|
||||||
subcmdFlushQueues = &cli.Command{
|
subcmdFlushQueues = cli.Command{
|
||||||
Name: "flush-queues",
|
Name: "flush-queues",
|
||||||
Usage: "Flush queues in the running process",
|
Usage: "Flush queues in the running process",
|
||||||
Action: runFlushQueues,
|
Action: runFlushQueues,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.DurationFlag{
|
cli.DurationFlag{
|
||||||
Name: "timeout",
|
Name: "timeout",
|
||||||
Value: 60 * time.Second,
|
Value: 60 * time.Second,
|
||||||
Usage: "Timeout for the flushing process",
|
Usage: "Timeout for the flushing process",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "non-blocking",
|
Name: "non-blocking",
|
||||||
Usage: "Set to true to not wait for flush to complete before returning",
|
Usage: "Set to true to not wait for flush to complete before returning",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
subCmdProcesses = &cli.Command{
|
subCmdProcesses = cli.Command{
|
||||||
Name: "processes",
|
Name: "processes",
|
||||||
Usage: "Display running processes within the current process",
|
Usage: "Display running processes within the current process",
|
||||||
Action: runProcesses,
|
Action: runProcesses,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "flat",
|
Name: "flat",
|
||||||
Usage: "Show processes as flat table rather than as tree",
|
Usage: "Show processes as flat table rather than as tree",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "no-system",
|
Name: "no-system",
|
||||||
Usage: "Do not show system processes",
|
Usage: "Do not show system processes",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "stacktraces",
|
Name: "stacktraces",
|
||||||
Usage: "Show stacktraces",
|
Usage: "Show stacktraces",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "json",
|
Name: "json",
|
||||||
Usage: "Output as json",
|
Usage: "Output as json",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "cancel",
|
Name: "cancel",
|
||||||
Usage: "Process PID to cancel. (Only available for non-system processes.)",
|
Usage: "Process PID to cancel. (Only available for non-system processes.)",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,68 +4,55 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
defaultLoggingFlags = []cli.Flag{
|
defaultLoggingFlags = []cli.Flag{
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "logger",
|
Name: "logger",
|
||||||
Usage: `Logger name - will default to "default"`,
|
Usage: `Logger name - will default to "default"`,
|
||||||
},
|
}, cli.StringFlag{
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "writer",
|
Name: "writer",
|
||||||
Usage: "Name of the log writer - will default to mode",
|
Usage: "Name of the log writer - will default to mode",
|
||||||
},
|
}, cli.StringFlag{
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "level",
|
Name: "level",
|
||||||
Usage: "Logging level for the new logger",
|
Usage: "Logging level for the new logger",
|
||||||
},
|
}, cli.StringFlag{
|
||||||
&cli.StringFlag{
|
Name: "stacktrace-level, L",
|
||||||
Name: "stacktrace-level",
|
Usage: "Stacktrace logging level",
|
||||||
Aliases: []string{"L"},
|
}, cli.StringFlag{
|
||||||
Usage: "Stacktrace logging level",
|
Name: "flags, F",
|
||||||
},
|
Usage: "Flags for the logger",
|
||||||
&cli.StringFlag{
|
}, cli.StringFlag{
|
||||||
Name: "flags",
|
Name: "expression, e",
|
||||||
Aliases: []string{"F"},
|
Usage: "Matching expression for the logger",
|
||||||
Usage: "Flags for the logger",
|
}, cli.StringFlag{
|
||||||
},
|
Name: "prefix, p",
|
||||||
&cli.StringFlag{
|
Usage: "Prefix for the logger",
|
||||||
Name: "expression",
|
}, cli.BoolFlag{
|
||||||
Aliases: []string{"e"},
|
|
||||||
Usage: "Matching expression for the logger",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "prefix",
|
|
||||||
Aliases: []string{"p"},
|
|
||||||
Usage: "Prefix for the logger",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "color",
|
Name: "color",
|
||||||
Usage: "Use color in the logs",
|
Usage: "Use color in the logs",
|
||||||
},
|
}, cli.BoolFlag{
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
subcmdLogging = &cli.Command{
|
subcmdLogging = cli.Command{
|
||||||
Name: "logging",
|
Name: "logging",
|
||||||
Usage: "Adjust logging commands",
|
Usage: "Adjust logging commands",
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []cli.Command{
|
||||||
{
|
{
|
||||||
Name: "pause",
|
Name: "pause",
|
||||||
Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)",
|
Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -74,7 +61,7 @@ var (
|
|||||||
Name: "resume",
|
Name: "resume",
|
||||||
Usage: "Resume logging",
|
Usage: "Resume logging",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -83,7 +70,7 @@ var (
|
|||||||
Name: "release-and-reopen",
|
Name: "release-and-reopen",
|
||||||
Usage: "Cause Gitea to release and re-open files used for logging",
|
Usage: "Cause Gitea to release and re-open files used for logging",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -93,9 +80,9 @@ var (
|
|||||||
Usage: "Remove a logger",
|
Usage: "Remove a logger",
|
||||||
ArgsUsage: "[name] Name of logger to remove",
|
ArgsUsage: "[name] Name of logger to remove",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
}, &cli.StringFlag{
|
}, cli.StringFlag{
|
||||||
Name: "logger",
|
Name: "logger",
|
||||||
Usage: `Logger name - will default to "default"`,
|
Usage: `Logger name - will default to "default"`,
|
||||||
},
|
},
|
||||||
@@ -104,48 +91,32 @@ var (
|
|||||||
}, {
|
}, {
|
||||||
Name: "add",
|
Name: "add",
|
||||||
Usage: "Add a logger",
|
Usage: "Add a logger",
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []cli.Command{
|
||||||
{
|
{
|
||||||
Name: "file",
|
Name: "file",
|
||||||
Usage: "Add a file logger",
|
Usage: "Add a file logger",
|
||||||
Flags: append(defaultLoggingFlags, []cli.Flag{
|
Flags: append(defaultLoggingFlags, []cli.Flag{
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "filename",
|
Name: "filename, f",
|
||||||
Aliases: []string{"f"},
|
Usage: "Filename for the logger - this must be set.",
|
||||||
Usage: "Filename for the logger - this must be set.",
|
}, cli.BoolTFlag{
|
||||||
},
|
Name: "rotate, r",
|
||||||
&cli.BoolFlag{
|
Usage: "Rotate logs",
|
||||||
Name: "rotate",
|
}, cli.Int64Flag{
|
||||||
Aliases: []string{"r"},
|
Name: "max-size, s",
|
||||||
Usage: "Rotate logs",
|
Usage: "Maximum size in bytes before rotation",
|
||||||
Value: true,
|
}, cli.BoolTFlag{
|
||||||
},
|
Name: "daily, d",
|
||||||
&cli.Int64Flag{
|
Usage: "Rotate logs daily",
|
||||||
Name: "max-size",
|
}, cli.IntFlag{
|
||||||
Aliases: []string{"s"},
|
Name: "max-days, D",
|
||||||
Usage: "Maximum size in bytes before rotation",
|
Usage: "Maximum number of daily logs to keep",
|
||||||
},
|
}, cli.BoolTFlag{
|
||||||
&cli.BoolFlag{
|
Name: "compress, z",
|
||||||
Name: "daily",
|
Usage: "Compress rotated logs",
|
||||||
Aliases: []string{"d"},
|
}, cli.IntFlag{
|
||||||
Usage: "Rotate logs daily",
|
Name: "compression-level, Z",
|
||||||
Value: true,
|
Usage: "Compression level to use",
|
||||||
},
|
|
||||||
&cli.IntFlag{
|
|
||||||
Name: "max-days",
|
|
||||||
Aliases: []string{"D"},
|
|
||||||
Usage: "Maximum number of daily logs to keep",
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "compress",
|
|
||||||
Aliases: []string{"z"},
|
|
||||||
Usage: "Compress rotated logs",
|
|
||||||
Value: true,
|
|
||||||
},
|
|
||||||
&cli.IntFlag{
|
|
||||||
Name: "compression-level",
|
|
||||||
Aliases: []string{"Z"},
|
|
||||||
Usage: "Compression level to use",
|
|
||||||
},
|
},
|
||||||
}...),
|
}...),
|
||||||
Action: runAddFileLogger,
|
Action: runAddFileLogger,
|
||||||
@@ -153,25 +124,18 @@ var (
|
|||||||
Name: "conn",
|
Name: "conn",
|
||||||
Usage: "Add a net conn logger",
|
Usage: "Add a net conn logger",
|
||||||
Flags: append(defaultLoggingFlags, []cli.Flag{
|
Flags: append(defaultLoggingFlags, []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "reconnect-on-message",
|
Name: "reconnect-on-message, R",
|
||||||
Aliases: []string{"R"},
|
Usage: "Reconnect to host for every message",
|
||||||
Usage: "Reconnect to host for every message",
|
}, cli.BoolFlag{
|
||||||
},
|
Name: "reconnect, r",
|
||||||
&cli.BoolFlag{
|
Usage: "Reconnect to host when connection is dropped",
|
||||||
Name: "reconnect",
|
}, cli.StringFlag{
|
||||||
Aliases: []string{"r"},
|
Name: "protocol, P",
|
||||||
Usage: "Reconnect to host when connection is dropped",
|
Usage: "Set protocol to use: tcp, unix, or udp (defaults to tcp)",
|
||||||
},
|
}, cli.StringFlag{
|
||||||
&cli.StringFlag{
|
Name: "address, a",
|
||||||
Name: "protocol",
|
Usage: "Host address and port to connect to (defaults to :7020)",
|
||||||
Aliases: []string{"P"},
|
|
||||||
Usage: "Set protocol to use: tcp, unix, or udp (defaults to tcp)",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "address",
|
|
||||||
Aliases: []string{"a"},
|
|
||||||
Usage: "Host address and port to connect to (defaults to :7020)",
|
|
||||||
},
|
},
|
||||||
}...),
|
}...),
|
||||||
Action: runAddConnLogger,
|
Action: runAddConnLogger,
|
||||||
@@ -181,10 +145,9 @@ var (
|
|||||||
Name: "log-sql",
|
Name: "log-sql",
|
||||||
Usage: "Set LogSQL",
|
Usage: "Set LogSQL",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "debug",
|
||||||
},
|
}, cli.BoolFlag{
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "off",
|
Name: "off",
|
||||||
Usage: "Switch off SQL logging",
|
Usage: "Switch off SQL logging",
|
||||||
},
|
},
|
||||||
@@ -250,7 +213,7 @@ func runAddFileLogger(c *cli.Context) error {
|
|||||||
if c.IsSet("filename") {
|
if c.IsSet("filename") {
|
||||||
vals["filename"] = c.String("filename")
|
vals["filename"] = c.String("filename")
|
||||||
} else {
|
} else {
|
||||||
return errors.New("filename must be set when creating a file logger")
|
return fmt.Errorf("filename must be set when creating a file logger")
|
||||||
}
|
}
|
||||||
if c.IsSet("rotate") {
|
if c.IsSet("rotate") {
|
||||||
vals["rotate"] = c.Bool("rotate")
|
vals["rotate"] = c.Bool("rotate")
|
||||||
|
|||||||
@@ -7,18 +7,18 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
"code.gitea.io/gitea/models/migrations"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/services/versioned_migration"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdMigrate represents the available migrate sub-command.
|
// CmdMigrate represents the available migrate sub-command.
|
||||||
var CmdMigrate = &cli.Command{
|
var CmdMigrate = cli.Command{
|
||||||
Name: "migrate",
|
Name: "migrate",
|
||||||
Usage: "Migrate the database",
|
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.`,
|
Description: "This is a command for migrating the database, so that you can run gitea admin create-user before starting the server.",
|
||||||
Action: runMigrate,
|
Action: runMigrate,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ func runMigrate(ctx *cli.Context) error {
|
|||||||
log.Info("Log path: %s", setting.Log.RootPath)
|
log.Info("Log path: %s", setting.Log.RootPath)
|
||||||
log.Info("Configuration file: %s", setting.CustomConf)
|
log.Info("Configuration file: %s", setting.CustomConf)
|
||||||
|
|
||||||
if err := db.InitEngineWithMigration(context.Background(), versioned_migration.Migrate); err != nil {
|
if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
|
||||||
log.Fatal("Failed to initialize ORM engine: %v", err)
|
log.Fatal("Failed to initialize ORM engine: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,13 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
actions_model "code.gitea.io/gitea/models/actions"
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
|
"code.gitea.io/gitea/models/migrations"
|
||||||
packages_model "code.gitea.io/gitea/models/packages"
|
packages_model "code.gitea.io/gitea/models/packages"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
@@ -20,111 +19,75 @@ import (
|
|||||||
packages_module "code.gitea.io/gitea/modules/packages"
|
packages_module "code.gitea.io/gitea/modules/packages"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/storage"
|
"code.gitea.io/gitea/modules/storage"
|
||||||
"code.gitea.io/gitea/services/versioned_migration"
|
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdMigrateStorage represents the available migrate storage sub-command.
|
// CmdMigrateStorage represents the available migrate storage sub-command.
|
||||||
var CmdMigrateStorage = &cli.Command{
|
var CmdMigrateStorage = cli.Command{
|
||||||
Name: "migrate-storage",
|
Name: "migrate-storage",
|
||||||
Usage: "Migrate the storage",
|
Usage: "Migrate the storage",
|
||||||
Description: "Copies stored files from storage configured in app.ini to parameter-configured storage",
|
Description: "Copies stored files from storage configured in app.ini to parameter-configured storage",
|
||||||
Action: runMigrateStorage,
|
Action: runMigrateStorage,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "type",
|
Name: "type, t",
|
||||||
Aliases: []string{"t"},
|
Value: "",
|
||||||
Value: "",
|
Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log'",
|
||||||
Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages', 'actions-log', 'actions-artifacts",
|
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "storage",
|
Name: "storage, s",
|
||||||
Aliases: []string{"s"},
|
Value: "",
|
||||||
Value: "",
|
Usage: "New storage type: local (default) or minio",
|
||||||
Usage: "New storage type: local (default), minio or azureblob",
|
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "path",
|
Name: "path, p",
|
||||||
Aliases: []string{"p"},
|
Value: "",
|
||||||
Value: "",
|
Usage: "New storage placement if store is local (leave blank for default)",
|
||||||
Usage: "New storage placement if store is local (leave blank for default)",
|
|
||||||
},
|
},
|
||||||
// Minio Storage special configurations
|
cli.StringFlag{
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "minio-endpoint",
|
Name: "minio-endpoint",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "Minio storage endpoint",
|
Usage: "Minio storage endpoint",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "minio-access-key-id",
|
Name: "minio-access-key-id",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "Minio storage accessKeyID",
|
Usage: "Minio storage accessKeyID",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "minio-secret-access-key",
|
Name: "minio-secret-access-key",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "Minio storage secretAccessKey",
|
Usage: "Minio storage secretAccessKey",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "minio-bucket",
|
Name: "minio-bucket",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "Minio storage bucket",
|
Usage: "Minio storage bucket",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "minio-location",
|
Name: "minio-location",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "Minio storage location to create bucket",
|
Usage: "Minio storage location to create bucket",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "minio-base-path",
|
Name: "minio-base-path",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "Minio storage base path on the bucket",
|
Usage: "Minio storage base path on the bucket",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "minio-use-ssl",
|
Name: "minio-use-ssl",
|
||||||
Usage: "Enable SSL for minio",
|
Usage: "Enable SSL for minio",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "minio-insecure-skip-verify",
|
Name: "minio-insecure-skip-verify",
|
||||||
Usage: "Skip SSL verification",
|
Usage: "Skip SSL verification",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "minio-checksum-algorithm",
|
Name: "minio-checksum-algorithm",
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "Minio checksum algorithm (default/md5)",
|
Usage: "Minio checksum algorithm (default/md5)",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "minio-bucket-lookup-type",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Minio bucket lookup type",
|
|
||||||
},
|
|
||||||
// Azure Blob Storage special configurations
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "azureblob-endpoint",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Azure Blob storage endpoint",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "azureblob-account-name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Azure Blob storage account name",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "azureblob-account-key",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Azure Blob storage account key",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "azureblob-container",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Azure Blob storage container",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "azureblob-base-path",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Azure Blob storage base path",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,9 +107,6 @@ func migrateLFS(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
|||||||
|
|
||||||
func migrateAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
func migrateAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||||
return db.Iterate(ctx, nil, func(ctx context.Context, user *user_model.User) error {
|
return db.Iterate(ctx, nil, func(ctx context.Context, user *user_model.User) error {
|
||||||
if user.CustomAvatarRelativePath() == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
_, err := storage.Copy(dstStorage, user.CustomAvatarRelativePath(), storage.Avatars, user.CustomAvatarRelativePath())
|
_, err := storage.Copy(dstStorage, user.CustomAvatarRelativePath(), storage.Avatars, user.CustomAvatarRelativePath())
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
@@ -154,9 +114,6 @@ func migrateAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error
|
|||||||
|
|
||||||
func migrateRepoAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
func migrateRepoAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||||
return db.Iterate(ctx, nil, func(ctx context.Context, repo *repo_model.Repository) error {
|
return db.Iterate(ctx, nil, func(ctx context.Context, repo *repo_model.Repository) error {
|
||||||
if repo.CustomAvatarRelativePath() == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
_, err := storage.Copy(dstStorage, repo.CustomAvatarRelativePath(), storage.RepoAvatars, repo.CustomAvatarRelativePath())
|
_, err := storage.Copy(dstStorage, repo.CustomAvatarRelativePath(), storage.RepoAvatars, repo.CustomAvatarRelativePath())
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
@@ -194,25 +151,6 @@ func migrateActionsLog(ctx context.Context, dstStorage storage.ObjectStorage) er
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
|
||||||
return db.Iterate(ctx, nil, func(ctx context.Context, artifact *actions_model.ActionArtifact) error {
|
|
||||||
if artifact.Status == actions_model.ArtifactStatusExpired {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := storage.Copy(dstStorage, artifact.StoragePath, storage.ActionsArtifacts, artifact.StoragePath)
|
|
||||||
if err != nil {
|
|
||||||
// ignore files that do not exist
|
|
||||||
if errors.Is(err, fs.ErrNotExist) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func runMigrateStorage(ctx *cli.Context) error {
|
func runMigrateStorage(ctx *cli.Context) error {
|
||||||
stdCtx, cancel := installSignals()
|
stdCtx, cancel := installSignals()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -227,7 +165,7 @@ func runMigrateStorage(ctx *cli.Context) error {
|
|||||||
log.Info("Log path: %s", setting.Log.RootPath)
|
log.Info("Log path: %s", setting.Log.RootPath)
|
||||||
log.Info("Configuration file: %s", setting.CustomConf)
|
log.Info("Configuration file: %s", setting.CustomConf)
|
||||||
|
|
||||||
if err := db.InitEngineWithMigration(context.Background(), versioned_migration.Migrate); err != nil {
|
if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
|
||||||
log.Fatal("Failed to initialize ORM engine: %v", err)
|
log.Fatal("Failed to initialize ORM engine: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -244,7 +182,7 @@ func runMigrateStorage(ctx *cli.Context) error {
|
|||||||
case string(setting.LocalStorageType):
|
case string(setting.LocalStorageType):
|
||||||
p := ctx.String("path")
|
p := ctx.String("path")
|
||||||
if p == "" {
|
if p == "" {
|
||||||
log.Fatal("Path must be given when storage is local")
|
log.Fatal("Path must be given when storage is loal")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
dstStorage, err = storage.NewLocalStorage(
|
dstStorage, err = storage.NewLocalStorage(
|
||||||
@@ -266,19 +204,6 @@ func runMigrateStorage(ctx *cli.Context) error {
|
|||||||
UseSSL: ctx.Bool("minio-use-ssl"),
|
UseSSL: ctx.Bool("minio-use-ssl"),
|
||||||
InsecureSkipVerify: ctx.Bool("minio-insecure-skip-verify"),
|
InsecureSkipVerify: ctx.Bool("minio-insecure-skip-verify"),
|
||||||
ChecksumAlgorithm: ctx.String("minio-checksum-algorithm"),
|
ChecksumAlgorithm: ctx.String("minio-checksum-algorithm"),
|
||||||
BucketLookUpType: ctx.String("minio-bucket-lookup-type"),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
case string(setting.AzureBlobStorageType):
|
|
||||||
dstStorage, err = storage.NewAzureBlobStorage(
|
|
||||||
stdCtx,
|
|
||||||
&setting.Storage{
|
|
||||||
AzureBlobConfig: setting.AzureBlobStorageConfig{
|
|
||||||
Endpoint: ctx.String("azureblob-endpoint"),
|
|
||||||
AccountName: ctx.String("azureblob-account-name"),
|
|
||||||
AccountKey: ctx.String("azureblob-account-key"),
|
|
||||||
Container: ctx.String("azureblob-container"),
|
|
||||||
BasePath: ctx.String("azureblob-base-path"),
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
@@ -289,14 +214,13 @@ func runMigrateStorage(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
migratedMethods := map[string]func(context.Context, storage.ObjectStorage) error{
|
migratedMethods := map[string]func(context.Context, storage.ObjectStorage) error{
|
||||||
"attachments": migrateAttachments,
|
"attachments": migrateAttachments,
|
||||||
"lfs": migrateLFS,
|
"lfs": migrateLFS,
|
||||||
"avatars": migrateAvatars,
|
"avatars": migrateAvatars,
|
||||||
"repo-avatars": migrateRepoAvatars,
|
"repo-avatars": migrateRepoAvatars,
|
||||||
"repo-archivers": migrateRepoArchivers,
|
"repo-archivers": migrateRepoArchivers,
|
||||||
"packages": migratePackages,
|
"packages": migratePackages,
|
||||||
"actions-log": migrateActionsLog,
|
"actions-log": migrateActionsLog,
|
||||||
"actions-artifacts": migrateActionsArtifacts,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tp := strings.ToLower(ctx.String("type"))
|
tp := strings.ToLower(ctx.String("type"))
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user