Compare commits
228 Commits
forgejo
...
release/v1
Author | SHA1 | Date |
---|---|---|
Andrew Thornton | a93d5e12d4 | |
Andrew Thornton | abe2fd614e | |
Andrew Thornton | f5e19dcfd7 | |
zeripath | 3182d4edaa | |
zeripath | f8deb116cc | |
techknowlogick | 5a16cbdaf6 | |
techknowlogick | 889bdf9196 | |
zeripath | 147bcc3d0f | |
Lunny Xiao | ce272f2e53 | |
zeripath | 9d9ad1b59f | |
Lunny Xiao | df57524c49 | |
Gusted | d60b5f1e89 | |
wxiaoguang | f7f4129f52 | |
zeripath | 21838225ab | |
wxiaoguang | 95f39457de | |
wxiaoguang | 6cb5069bf6 | |
Richard Mahn | a6f9ebfeb9 | |
Lunny Xiao | 14de28b876 | |
Norwin | e4120bbc89 | |
Lunny Xiao | 37abfcaf8a | |
silverwind | c719841f0d | |
silverwind | f9e150002e | |
zeripath | 2f4f2852fc | |
Lunny Xiao | b25a571bc9 | |
Gusted | f9bbed028c | |
Lunny Xiao | 7e084341fe | |
DuckDuckWhale | 7d75eede04 | |
zeripath | 3db98bef99 | |
DuckDuckWhale | 484fe075f4 | |
zeripath | de3216ee55 | |
Lunny Xiao | 353d88a42e | |
Lunny Xiao | a17fce31a9 | |
zeripath | 71e1ebfa60 | |
zeripath | afe9d2cadd | |
zeripath | 012e45a4c1 | |
zeripath | d25ff0d695 | |
zeripath | 6eaebda1b5 | |
zeripath | 6100935a77 | |
zeripath | 6de75224de | |
Lunny Xiao | 9086916eb7 | |
zeripath | 877040e652 | |
Lunny Xiao | 91f5be889a | |
zeripath | a818a48c76 | |
zeripath | 76e1c130fb | |
zeripath | 148a417774 | |
zeripath | 6081948ef0 | |
zeripath | 48bd54286c | |
zeripath | c69b3b65f3 | |
zeripath | fe91d9617b | |
Gusted | 711ca52f1f | |
Lunny Xiao | a15f0cb010 | |
zeripath | 2051f850ef | |
zeripath | 3ae4c4898b | |
zeripath | 3a77465e4e | |
zeripath | fc8c23edb7 | |
KN4CK3R | 31df892059 | |
Lunny Xiao | 9879e23c57 | |
Lunny Xiao | 56a3b50136 | |
a1012112796 | 9a8532d928 | |
Lunny Xiao | d29a0fc3be | |
Gusted | 04517e17d6 | |
KN4CK3R | 3a222ee416 | |
silverwind | add85f5a85 | |
Gusted | 76ad83f05e | |
Lunny Xiao | 714ecd9f1e | |
Lunny Xiao | a08856606e | |
99rgosse | 7be2d7b136 | |
Lunny Xiao | 6f3596e33c | |
Lunny Xiao | 0305a73633 | |
wxiaoguang | 6cd1ccef3d | |
Gusted | ea0fe83888 | |
Lunny Xiao | 1cec7f5ab5 | |
Gusted | 1cb1101d44 | |
zeripath | 653dff4e57 | |
Gusted | b661bbaed7 | |
wxiaoguang | 20ae184967 | |
zeripath | 15b44496ec | |
Gusted | 0d0ff5e32a | |
Lunny Xiao | f25f7c592f | |
delvh | e8cf04bad7 | |
Lunny Xiao | 251fdaaf41 | |
Lunny Xiao | f572fb906f | |
zeripath | 9340269d84 | |
zeripath | 34650b925b | |
zeripath | 718e0db12e | |
qwerty287 | 6110ddc280 | |
zeripath | c7d8181a70 | |
wxiaoguang | 548ae3eb98 | |
zeripath | 2c383d812d | |
zeripath | ef12b8de80 | |
zeripath | dd1ba34ee5 | |
KN4CK3R | 1fbdf96c34 | |
KN4CK3R | 5159055278 | |
wxiaoguang | 06da10b9a1 | |
wxiaoguang | 175ebc6f88 | |
zeripath | 3aecea2e6e | |
zeripath | cae8c63517 | |
zeripath | 8ace5c1161 | |
Lunny Xiao | a87b813955 | |
6543 | 3baeec745c | |
Richard Mahn | befb6bea22 | |
6543 | 79f0b1a50b | |
zeripath | 79a3d277e5 | |
John Olheiser | eb748ff79e | |
Mario Lubenka | c5770195d9 | |
zeripath | a20ccec369 | |
zeripath | 9c2b7a196e | |
zeripath | 1e278b15c2 | |
Viktor Kuzmin | fde6ff6a75 | |
zeripath | 51f4f8c393 | |
Matti R | f5845e6497 | |
Norwin | c927ebd119 | |
Norwin | 245596e130 | |
Jimmy Praet | 1c3ae6d05e | |
Jimmy Praet | a1e57ebe6b | |
6543 | 73ae93b007 | |
6543 | dc030f64a7 | |
Jimmy Praet | 6e0a08d753 | |
pricly-yellow | 7b1153e943 | |
pricly-yellow | 6995be66e7 | |
6543 | 28971c7c15 | |
Lunny Xiao | eb5e6f09eb | |
Alexey 〒erentyev | bf6264c1db | |
zeripath | 5b6b7e79cf | |
Lunny Xiao | 766272b154 | |
zeripath | 4707d4b8a9 | |
zeripath | 4b8b214108 | |
zeripath | ebae7e1512 | |
6543 | 122917f4d5 | |
Alexey 〒erentyev | 9cf5739c0f | |
zeripath | 4b6556565f | |
zeripath | 7ce938b6c7 | |
zeripath | 6139834e76 | |
zeripath | b673a24ee6 | |
Lunny Xiao | fd35f56e87 | |
Lunny Xiao | 1f8df5dd89 | |
zeripath | 6a025d8b4a | |
zeripath | 270c7f36db | |
zeripath | 0e448fb96d | |
zeripath | 659b946eda | |
KN4CK3R | 56ab5ec9ea | |
6543 | 3b13c5d41a | |
6543 | d27f061863 | |
Lunny Xiao | 07489d0405 | |
Prasad Katti | 30708d9ffe | |
silverwind | 1b08dfeacf | |
zeripath | e5ded0ee19 | |
zeripath | a384109244 | |
zeripath | 67ceb61fe3 | |
zeripath | 5cb5101720 | |
zeripath | 6f261fdf47 | |
zeripath | 3365611f06 | |
zeripath | ceae89c8c7 | |
zeripath | 8f300781ad | |
Lunny Xiao | 8b132bdec6 | |
wxiaoguang | e70b679d21 | |
zeripath | 02de43236a | |
zeripath | f949f9e9c5 | |
6543 | cbe3ca5d0b | |
6543 | 3ac1f35349 | |
Lunny Xiao | af1fd56d8a | |
6543 | 0274933c22 | |
6543 | 67776372d6 | |
6543 | c54639b8ee | |
6543 | 49a71a6461 | |
zeripath | 58f4a4114e | |
zeripath | b31307c41c | |
6543 | c5193a8481 | |
zeripath | 1790f01dd9 | |
techknowlogick | 0c7927fe48 | |
zeripath | 695e8ae81d | |
zeripath | 2148b27bfa | |
Lunny Xiao | ab7e36e3a5 | |
zeripath | 63178b5654 | |
zeripath | 2d15126de6 | |
zeripath | 31e4e8205a | |
zeripath | 37e4cdbbe6 | |
techknowlogick | f7e7477c45 | |
KN4CK3R | 0840a508b4 | |
zeripath | 5ceff8fda2 | |
Steven | 778a0bf758 | |
Elouan Martinet | f19ccd8f6a | |
KN4CK3R | b6e4688874 | |
zeripath | 25437672bf | |
techknowlogick | 0dc808212b | |
Lunny Xiao | b6508b872b | |
zeripath | d89029ebac | |
6543 | 62315ea731 | |
6543 | 86861ee135 | |
6543 | d2d99a25b7 | |
nitul1991 | e483ec8b0d | |
zeripath | 46d62ad896 | |
zeripath | 428d58f8da | |
zeripath | 1a2256bf44 | |
techknowlogick | 20601f8463 | |
zeripath | 619e6d6400 | |
zeripath | f438b6f33b | |
zeripath | c47065cc29 | |
zeripath | 719e2f26d5 | |
zeripath | 40687a2160 | |
zeripath | f9120092c1 | |
zeripath | a17edf446f | |
zeripath | ff8fadd2be | |
zeripath | 5fe7c0ed7b | |
6543 | 763e4196ba | |
zeripath | 903bdefb58 | |
Lunny Xiao | 840d240a61 | |
a1012112796 | 7365b4e757 | |
6543 | e10cd3da1e | |
zeripath | 693275455e | |
6543 | 91527434d0 | |
Lunny Xiao | 89f680aa04 | |
zeripath | 67942ac1a9 | |
zeripath | 0b06b2019f | |
zeripath | 057205a4b7 | |
zeripath | 1b6c0c6bdc | |
6543 | c4f3f5bdf2 | |
zeripath | 1f5011dff7 | |
zeripath | cf9aeca508 | |
6543 | 09a4364b21 | |
zeripath | 0c3467ffb7 | |
Gary Wang | d268c9d6e1 | |
zeripath | 7f6019e492 | |
zeripath | 0f11c5f592 | |
zeripath | bae0e1d773 | |
techknowlogick | 0877d497f3 | |
zeripath | e5fde7ef00 | |
zeripath | 6243638c11 |
|
@ -0,0 +1,9 @@
|
|||
root = "."
|
||||
tmp_dir = ".air"
|
||||
|
||||
[build]
|
||||
cmd = "make backend"
|
||||
bin = "gitea"
|
||||
include_ext = ["go", "tmpl"]
|
||||
exclude_dir = ["modules/git/tests", "services/gitdiff/testdata", "modules/avatar/testdata"]
|
||||
include_dir = ["cmd", "models", "modules", "options", "routers", "services", "templates"]
|
10
.air.toml
10
.air.toml
|
@ -1,10 +0,0 @@
|
|||
root = "."
|
||||
tmp_dir = ".air"
|
||||
|
||||
[build]
|
||||
cmd = "make backend"
|
||||
bin = "gitea"
|
||||
include_ext = ["go", "tmpl"]
|
||||
exclude_dir = ["modules/git/tests", "services/gitdiff/testdata", "modules/avatar/testdata"]
|
||||
include_dir = ["cmd", "models", "modules", "options", "routers", "services", "templates"]
|
||||
exclude_regex = ["_test.go$", "_gen.go$"]
|
114
.dockerignore
114
.dockerignore
|
@ -1,114 +0,0 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# IntelliJ
|
||||
.idea
|
||||
# Goland's output filename can not be set manually
|
||||
/go_build_*
|
||||
|
||||
# MS VSCode
|
||||
.vscode
|
||||
__debug_bin
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
*coverage.out
|
||||
coverage.all
|
||||
cpu.out
|
||||
|
||||
/modules/migration/bindata.go
|
||||
/modules/migration/bindata.go.hash
|
||||
/modules/options/bindata.go
|
||||
/modules/options/bindata.go.hash
|
||||
/modules/public/bindata.go
|
||||
/modules/public/bindata.go.hash
|
||||
/modules/templates/bindata.go
|
||||
/modules/templates/bindata.go.hash
|
||||
|
||||
*.db
|
||||
*.log
|
||||
|
||||
/gitea
|
||||
/gitea-vet
|
||||
/debug
|
||||
/integrations.test
|
||||
|
||||
/bin
|
||||
/dist
|
||||
/custom/*
|
||||
!/custom/conf
|
||||
/custom/conf/*
|
||||
!/custom/conf/app.example.ini
|
||||
/data
|
||||
/indexers
|
||||
/log
|
||||
/public/img/avatar
|
||||
/tests/integration/gitea-integration-*
|
||||
/tests/integration/indexers-*
|
||||
/tests/e2e/gitea-e2e-*
|
||||
/tests/e2e/indexers-*
|
||||
/tests/e2e/reports
|
||||
/tests/e2e/test-artifacts
|
||||
/tests/e2e/test-snapshots
|
||||
/tests/*.ini
|
||||
/node_modules
|
||||
/yarn.lock
|
||||
/yarn-error.log
|
||||
/npm-debug.log*
|
||||
/public/js
|
||||
/public/serviceworker.js
|
||||
/public/css
|
||||
/public/fonts
|
||||
/public/img/webpack
|
||||
/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
|
||||
/.air
|
||||
/.go-licenses
|
||||
|
||||
# Snapcraft
|
||||
snap/.snapcraft/
|
||||
parts/
|
||||
stage/
|
||||
prime/
|
||||
*.snap
|
||||
*.snap-build
|
||||
*_source.tar.bz2
|
||||
.DS_Store
|
||||
|
||||
# Make evidence files
|
||||
/.make_evidence
|
||||
|
||||
# Manpage
|
||||
/man
|
555
.drone.yml
555
.drone.yml
File diff suppressed because it is too large
Load Diff
|
@ -26,3 +26,6 @@ indent_style = tab
|
|||
|
||||
[*.svg]
|
||||
insert_final_newline = false
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
|
|
@ -0,0 +1,439 @@
|
|||
root: true
|
||||
reportUnusedDisableDirectives: true
|
||||
|
||||
ignorePatterns:
|
||||
- /web_src/js/vendor
|
||||
- /templates/base/head.tmpl
|
||||
- /templates/repo/activity.tmpl
|
||||
- /templates/repo/view_file.tmpl
|
||||
|
||||
parserOptions:
|
||||
sourceType: module
|
||||
ecmaVersion: 2021
|
||||
|
||||
plugins:
|
||||
- eslint-plugin-unicorn
|
||||
- eslint-plugin-import
|
||||
- eslint-plugin-vue
|
||||
- eslint-plugin-html
|
||||
|
||||
extends:
|
||||
- plugin:vue/recommended
|
||||
|
||||
env:
|
||||
es2021: true
|
||||
node: true
|
||||
|
||||
globals:
|
||||
__webpack_public_path__: true
|
||||
CodeMirror: false
|
||||
Dropzone: false
|
||||
SimpleMDE: false
|
||||
u2fApi: false
|
||||
|
||||
settings:
|
||||
html/html-extensions: [".tmpl"]
|
||||
|
||||
overrides:
|
||||
- files: ["web_src/**/*.js", "web_src/**/*.vue", "templates/**/*.tmpl"]
|
||||
env:
|
||||
browser: true
|
||||
jquery: true
|
||||
node: false
|
||||
- files: ["templates/**/*.tmpl"]
|
||||
rules:
|
||||
no-tabs: [0]
|
||||
indent: [2, tab, {SwitchCase: 1}]
|
||||
- files: ["web_src/**/*worker.js"]
|
||||
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, 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, status, statusbar, stop, toolbar, top]
|
||||
- files: ["build/generate-images.js"]
|
||||
rules:
|
||||
import/no-unresolved: [0]
|
||||
import/no-extraneous-dependencies: [0]
|
||||
- files: ["*.test.js"]
|
||||
env:
|
||||
jest: true
|
||||
- files: ["*.config.js"]
|
||||
rules:
|
||||
import/no-unused-modules: [0]
|
||||
|
||||
rules:
|
||||
accessor-pairs: [2]
|
||||
array-bracket-newline: [0]
|
||||
array-bracket-spacing: [2, never]
|
||||
array-callback-return: [0]
|
||||
array-element-newline: [0]
|
||||
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]
|
||||
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/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: [0]
|
||||
import/no-anonymous-default-export: [0]
|
||||
import/no-commonjs: [0]
|
||||
import/no-cycle: [2, {ignoreExternal: true}]
|
||||
import/no-default-export: [0]
|
||||
import/no-deprecated: [0]
|
||||
import/no-dynamic-require: [0]
|
||||
import/no-extraneous-dependencies: [2]
|
||||
import/no-internal-modules: [0]
|
||||
import/no-mutable-exports: [2]
|
||||
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-parent-imports: [0]
|
||||
import/no-restricted-paths: [0]
|
||||
import/no-self-import: [2]
|
||||
import/no-unassigned-import: [0]
|
||||
import/no-unresolved: [2, {commonjs: true}]
|
||||
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]
|
||||
key-spacing: [2]
|
||||
keyword-spacing: [2]
|
||||
line-comment-position: [0]
|
||||
linebreak-style: [2, unix]
|
||||
lines-around-comment: [0]
|
||||
lines-between-class-members: [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: [2]
|
||||
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: [info, warn, error]}]
|
||||
no-const-assign: [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: [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: [0]
|
||||
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-label-var: [2]
|
||||
no-labels: [2]
|
||||
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-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]
|
||||
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-vars: [2, {args: all, argsIgnorePattern: ^_, varsIgnorePattern: ^_, caughtErrorsIgnorePattern: ^_, ignoreRestSiblings: false}]
|
||||
no-use-before-define: [2, nofunc]
|
||||
no-useless-backreference: [0]
|
||||
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: [2]
|
||||
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}]
|
||||
prefer-destructuring: [0]
|
||||
prefer-exponentiation-operator: [2]
|
||||
prefer-named-capture-group: [0]
|
||||
prefer-numeric-literals: [2]
|
||||
prefer-object-spread: [0]
|
||||
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]
|
||||
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}]
|
||||
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-for-each: [0]
|
||||
unicorn/no-array-instanceof: [0]
|
||||
unicorn/no-array-push-push: [2]
|
||||
unicorn/no-console-spaces: [0]
|
||||
unicorn/no-document-cookie: [2]
|
||||
unicorn/no-fn-reference-in-iterator: [0]
|
||||
unicorn/no-for-loop: [0]
|
||||
unicorn/no-hex-escape: [0]
|
||||
unicorn/no-keyword-prefix: [0]
|
||||
unicorn/no-lonely-if: [2]
|
||||
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: [2]
|
||||
unicorn/no-process-exit: [0]
|
||||
unicorn/no-reduce: [2]
|
||||
unicorn/no-static-only-class: [2]
|
||||
unicorn/no-this-assignment: [2]
|
||||
unicorn/no-unreadable-array-destructuring: [0]
|
||||
unicorn/no-unsafe-regex: [0]
|
||||
unicorn/no-unused-properties: [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-dataset: [2]
|
||||
unicorn/prefer-date-now: [2]
|
||||
unicorn/prefer-default-parameters: [0]
|
||||
unicorn/prefer-event-key: [2]
|
||||
unicorn/prefer-includes: [2]
|
||||
unicorn/prefer-math-trunc: [2]
|
||||
unicorn/prefer-modern-dom-apis: [0]
|
||||
unicorn/prefer-module: [2]
|
||||
unicorn/prefer-negative-index: [2]
|
||||
unicorn/prefer-node-append: [0]
|
||||
unicorn/prefer-node-protocol: [0]
|
||||
unicorn/prefer-node-remove: [0]
|
||||
unicorn/prefer-number-properties: [0]
|
||||
unicorn/prefer-optional-catch-binding: [2]
|
||||
unicorn/prefer-query-selector: [0]
|
||||
unicorn/prefer-reflect-apply: [0]
|
||||
unicorn/prefer-regexp-test: [2]
|
||||
unicorn/prefer-replace-all: [0]
|
||||
unicorn/prefer-set-has: [0]
|
||||
unicorn/prefer-spread: [0]
|
||||
unicorn/prefer-starts-ends-with: [2]
|
||||
unicorn/prefer-string-slice: [0]
|
||||
unicorn/prefer-switch: [0]
|
||||
unicorn/prefer-ternary: [0]
|
||||
unicorn/prefer-text-content: [2]
|
||||
unicorn/prefer-trim-start-end: [2]
|
||||
unicorn/prefer-type-error: [0]
|
||||
unicorn/prevent-abbreviations: [0]
|
||||
unicorn/string-content: [0]
|
||||
unicorn/throw-new-error: [2]
|
||||
use-isnan: [2]
|
||||
valid-typeof: [2, {requireStringLiterals: true}]
|
||||
vars-on-top: [0]
|
||||
vue/attributes-order: [0]
|
||||
vue/component-definition-name-casing: [0]
|
||||
vue/html-closing-bracket-spacing: [0]
|
||||
vue/max-attributes-per-line: [0]
|
||||
vue/one-component-per-file: [0]
|
||||
wrap-iife: [2, inside]
|
||||
wrap-regex: [0]
|
||||
yield-star-spacing: [2, after]
|
||||
yoda: [2, never]
|
536
.eslintrc.yaml
536
.eslintrc.yaml
|
@ -1,536 +0,0 @@
|
|||
root: true
|
||||
reportUnusedDisableDirectives: true
|
||||
|
||||
ignorePatterns:
|
||||
- /web_src/js/vendor
|
||||
|
||||
parserOptions:
|
||||
sourceType: module
|
||||
ecmaVersion: latest
|
||||
|
||||
plugins:
|
||||
- eslint-plugin-unicorn
|
||||
- eslint-plugin-import
|
||||
- eslint-plugin-jquery
|
||||
- eslint-plugin-sonarjs
|
||||
|
||||
env:
|
||||
es2022: true
|
||||
node: true
|
||||
|
||||
globals:
|
||||
__webpack_public_path__: true
|
||||
|
||||
overrides:
|
||||
- files: ["web_src/**/*.js", "docs/**/*.js"]
|
||||
env:
|
||||
browser: true
|
||||
node: false
|
||||
- files: ["web_src/**/*worker.js"]
|
||||
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.js"]
|
||||
rules:
|
||||
import/no-unused-modules: [0]
|
||||
|
||||
rules:
|
||||
accessor-pairs: [2]
|
||||
array-bracket-newline: [0]
|
||||
array-bracket-spacing: [2, never]
|
||||
array-callback-return: [2, {checkForEach: true}]
|
||||
array-element-newline: [0]
|
||||
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]
|
||||
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/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: [0]
|
||||
import/no-anonymous-default-export: [0]
|
||||
import/no-commonjs: [0]
|
||||
import/no-cycle: [2, {ignoreExternal: true, maxDepth: 1}]
|
||||
import/no-default-export: [0]
|
||||
import/no-deprecated: [0]
|
||||
import/no-dynamic-require: [0]
|
||||
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: [0]
|
||||
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: [0]
|
||||
jquery/no-serialize: [2]
|
||||
jquery/no-show: [0]
|
||||
jquery/no-size: [2]
|
||||
jquery/no-sizzle: [0]
|
||||
jquery/no-slide: [0]
|
||||
jquery/no-submit: [0]
|
||||
jquery/no-text: [0]
|
||||
jquery/no-toggle: [0]
|
||||
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-label-var: [2]
|
||||
no-labels: [0]
|
||||
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-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]
|
||||
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]
|
||||
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: [0]
|
||||
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-for-each: [2]
|
||||
unicorn/no-array-instanceof: [0]
|
||||
unicorn/no-array-method-this-argument: [2]
|
||||
unicorn/no-array-push-push: [2]
|
||||
unicorn/no-await-expression-member: [0]
|
||||
unicorn/no-console-spaces: [0]
|
||||
unicorn/no-document-cookie: [2]
|
||||
unicorn/no-empty-file: [2]
|
||||
unicorn/no-fn-reference-in-iterator: [0]
|
||||
unicorn/no-for-loop: [0]
|
||||
unicorn/no-hex-escape: [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-reduce: [2]
|
||||
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-code-point: [0]
|
||||
unicorn/prefer-dataset: [2]
|
||||
unicorn/prefer-date-now: [2]
|
||||
unicorn/prefer-default-parameters: [0]
|
||||
unicorn/prefer-event-key: [2]
|
||||
unicorn/prefer-event-target: [2]
|
||||
unicorn/prefer-export-from: [2]
|
||||
unicorn/prefer-includes: [2]
|
||||
unicorn/prefer-json-parse-buffer: [0]
|
||||
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-append: [0]
|
||||
unicorn/prefer-node-protocol: [0]
|
||||
unicorn/prefer-node-remove: [0]
|
||||
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-replace-all: [0]
|
||||
unicorn/prefer-set-has: [0]
|
||||
unicorn/prefer-set-size: [2]
|
||||
unicorn/prefer-spread: [0]
|
||||
unicorn/prefer-starts-ends-with: [2]
|
||||
unicorn/prefer-string-slice: [0]
|
||||
unicorn/prefer-switch: [0]
|
||||
unicorn/prefer-ternary: [0]
|
||||
unicorn/prefer-text-content: [2]
|
||||
unicorn/prefer-top-level-await: [0]
|
||||
unicorn/prefer-trim-start-end: [2]
|
||||
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]
|
||||
wrap-iife: [2, inside]
|
||||
wrap-regex: [0]
|
||||
yield-star-spacing: [2, after]
|
||||
yoda: [2, never]
|
|
@ -1,8 +1,6 @@
|
|||
* text=auto eol=lf
|
||||
*.tmpl linguist-language=Handlebars
|
||||
/assets/*.json linguist-generated
|
||||
/public/vendor/** -text -eol linguist-vendored
|
||||
/vendor/** -text -eol linguist-vendored
|
||||
/web_src/fomantic/build/** linguist-generated
|
||||
/web_src/js/vendor/** -text -eol linguist-vendored
|
||||
Dockerfile.* linguist-language=Dockerfile
|
||||
/public/vendor/** -text -eol linguist-vendored
|
||||
/templates/**/*.tmpl linguist-language=Handlebars
|
||||
/.eslintrc linguist-language=YAML
|
||||
/.stylelintrc linguist-language=YAML
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
---
|
||||
name: "Bug Report"
|
||||
about: "Found something you weren't expecting? Report it here!"
|
||||
title: "[BUG] "
|
||||
---
|
||||
<!--
|
||||
NOTE: If your issue is a security concern, please email security@forgejo.org (GPG: A4676E79) instead of opening a public issue.
|
||||
|
||||
1. Please speak English, as this is the language all maintainers can
|
||||
speak and write.
|
||||
|
||||
2. Please ask questions or troubleshoot configuration/deploy problems
|
||||
in our Matrix space (https://matrix.to/#/#forgejo:matrix.org).
|
||||
|
||||
3. Please make sure you are using the latest release of Forgejo and
|
||||
take a moment to check that your issue hasn't been reported before.
|
||||
|
||||
4. Please give all relevant information below for bug reports, because
|
||||
incomplete details will be handled as an invalid report.
|
||||
|
||||
5. If you are using a proxy or a CDN (e.g. CloudFlare) in front of
|
||||
Forgejo, please disable the proxy/CDN fully and connect to Forgejo
|
||||
directly to confirm the issue still persists without those services.
|
||||
-->
|
||||
|
||||
- Forgejo version (or commit ref):
|
||||
- Git version:
|
||||
- Operating system:
|
||||
- Database (use `[x]`):
|
||||
- [ ] PostgreSQL
|
||||
- [ ] MySQL
|
||||
- [ ] MSSQL
|
||||
- [ ] SQLite
|
||||
- How are you running Forgejo?
|
||||
<!--
|
||||
Please include information on whether you built Forgejo yourself, used one of our downloads, or are using some other package.
|
||||
Please also tell us how you are running Forgejo, e.g. if it is being run from docker, a command-line, systemd etc.
|
||||
If you are using a package or systemd tell us what distribution you are using.
|
||||
-->
|
||||
|
||||
## Description
|
||||
<!-- Please describe the issue you are having as clearly and succinctly as possible. -->
|
||||
|
||||
## Logs
|
||||
<!--
|
||||
It is really important to provide pertinent logs. We need DEBUG level logs.
|
||||
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`.
|
||||
Please copy and paste your logs here, with any sensitive information (e.g. API keys) removed/hidden.
|
||||
-->
|
||||
|
||||
## Screenshots
|
||||
<!-- If this issue involves the Web Interface, please provide one or more screenshots -->
|
|
@ -1,21 +0,0 @@
|
|||
---
|
||||
name: "Feature Request"
|
||||
about: "Got an idea for a feature that Forgejo doesn't have yet? Submit it here!"
|
||||
title: "[FEAT] "
|
||||
---
|
||||
<!--
|
||||
1. Please speak English, as this is the language all maintainers can
|
||||
speak and write.
|
||||
|
||||
2. Please ask questions or troubleshoot configuration/deploy problems
|
||||
in our Matrix space (https://matrix.to/#/#forgejo:matrix.org).
|
||||
|
||||
3. Please make sure you are using the latest release of Forgejo and
|
||||
take a moment to check that your feature hasn't already been suggested.
|
||||
-->
|
||||
|
||||
## Feature Description
|
||||
<!-- Please describe the feature you would like to see added as clearly and succinctly as possible. -->
|
||||
|
||||
## Screenshots
|
||||
<!-- If you can, provide screenshots of an implementation on another site, e.g. GitHub. -->
|
|
@ -1,4 +0,0 @@
|
|||
<!--
|
||||
Before submitting a PR, please read the contributing guidelines:
|
||||
https://codeberg.org/forgejo/forgejo/src/branch/main/CONTRIBUTING.md
|
||||
-->
|
|
@ -0,0 +1,2 @@
|
|||
open_collective: gitea
|
||||
custom: https://www.bountysource.com/teams/gitea
|
|
@ -0,0 +1,42 @@
|
|||
<!-- NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue -->
|
||||
|
||||
<!--
|
||||
1. Please speak English, this is the language all maintainers can speak and write.
|
||||
2. Please ask questions or configuration/deploy problems on our Discord
|
||||
server (https://discord.gg/gitea) or forum (https://discourse.gitea.io).
|
||||
3. Please take a moment to check that your issue doesn't already exist.
|
||||
4. 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
|
||||
incomplete details will be handled as an invalid report.
|
||||
-->
|
||||
|
||||
- Gitea version (or commit ref):
|
||||
- Git version:
|
||||
- Operating system:
|
||||
<!-- Please include information on whether you built gitea yourself, used one of our downloads or are using some other package -->
|
||||
<!-- Please also tell us how you are running gitea, e.g. if it is being run from docker, a command-line, systemd etc. --->
|
||||
<!-- If you are using a package or systemd tell us what distribution you are using -->
|
||||
- Database (use `[x]`):
|
||||
- [ ] PostgreSQL
|
||||
- [ ] MySQL
|
||||
- [ ] MSSQL
|
||||
- [ ] SQLite
|
||||
- Can you reproduce the bug at https://try.gitea.io:
|
||||
- [ ] Yes (provide example URL)
|
||||
- [ ] No
|
||||
- Log gist:
|
||||
<!-- It really is important to provide pertinent logs -->
|
||||
<!-- 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 -->
|
||||
|
||||
## Description
|
||||
<!-- 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. -->
|
||||
|
||||
...
|
||||
|
||||
|
||||
## Screenshots
|
||||
|
||||
<!-- **If this issue involves the Web Interface, please include a screenshot** -->
|
|
@ -0,0 +1,23 @@
|
|||
# Configuration for Lock Threads - https://github.com/dessant/lock-threads-app
|
||||
|
||||
# Number of days of inactivity before a closed issue or pull request is locked
|
||||
daysUntilLock: 60
|
||||
|
||||
# Skip issues and pull requests created before a given timestamp. Timestamp must
|
||||
# follow ISO 8601 (`YYYY-MM-DD`). `false` is disabled
|
||||
skipCreatedBefore: false
|
||||
|
||||
# Issues and pull requests with these labels will be ignored.
|
||||
exemptLabels: []
|
||||
|
||||
# Label to add before locking, such as `outdated`. `false` is disabled
|
||||
lockLabel: false
|
||||
|
||||
# Comment to post before locking.
|
||||
lockComment: >
|
||||
This thread has been automatically locked since there has not been
|
||||
any recent activity after it was closed. Please open a new issue for
|
||||
related bugs and link to relevant comments in this thread.
|
||||
|
||||
# Assign `resolved` as the reason for locking. Set to `false` to disable
|
||||
setLockReason: true
|
|
@ -0,0 +1,7 @@
|
|||
Please check the following:
|
||||
|
||||
1. Make sure you are targeting the `main` branch, pull requests on release branches are only allowed for bug fixes.
|
||||
2. Read contributing guidelines: https://github.com/go-gitea/gitea/blob/master/CONTRIBUTING.md
|
||||
3. Describe what your pull request does and which issue you're targeting (if any)
|
||||
|
||||
**You MUST delete the content above including this line before posting, otherwise your pull request will be invalid.**
|
|
@ -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.
|
|
@ -9,8 +9,6 @@ _test
|
|||
|
||||
# IntelliJ
|
||||
.idea
|
||||
# Goland's output filename can not be set manually
|
||||
/go_build_*
|
||||
|
||||
# MS VSCode
|
||||
.vscode
|
||||
|
@ -36,8 +34,6 @@ _testmain.go
|
|||
coverage.all
|
||||
cpu.out
|
||||
|
||||
/modules/migration/bindata.go
|
||||
/modules/migration/bindata.go.hash
|
||||
/modules/options/bindata.go
|
||||
/modules/options/bindata.go.hash
|
||||
/modules/public/bindata.go
|
||||
|
@ -63,14 +59,21 @@ cpu.out
|
|||
/indexers
|
||||
/log
|
||||
/public/img/avatar
|
||||
/tests/integration/gitea-integration-*
|
||||
/tests/integration/indexers-*
|
||||
/tests/e2e/gitea-e2e-*
|
||||
/tests/e2e/indexers-*
|
||||
/tests/e2e/reports
|
||||
/tests/e2e/test-artifacts
|
||||
/tests/e2e/test-snapshots
|
||||
/tests/*.ini
|
||||
/integrations/gitea-integration-mysql
|
||||
/integrations/gitea-integration-mysql8
|
||||
/integrations/gitea-integration-pgsql
|
||||
/integrations/gitea-integration-sqlite
|
||||
/integrations/gitea-integration-mssql
|
||||
/integrations/indexers-mysql
|
||||
/integrations/indexers-mysql8
|
||||
/integrations/indexers-pgsql
|
||||
/integrations/indexers-sqlite
|
||||
/integrations/indexers-mssql
|
||||
/integrations/sqlite.ini
|
||||
/integrations/mysql.ini
|
||||
/integrations/mysql8.ini
|
||||
/integrations/pgsql.ini
|
||||
/integrations/mssql.ini
|
||||
/node_modules
|
||||
/yarn.lock
|
||||
/yarn-error.log
|
||||
|
@ -80,7 +83,6 @@ cpu.out
|
|||
/public/css
|
||||
/public/fonts
|
||||
/public/img/webpack
|
||||
/vendor
|
||||
/web_src/fomantic/node_modules
|
||||
/web_src/fomantic/build/*
|
||||
!/web_src/fomantic/build/semantic.js
|
||||
|
@ -95,7 +97,6 @@ cpu.out
|
|||
!/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
|
||||
/VERSION
|
||||
/.air
|
||||
/.go-licenses
|
||||
|
||||
# Snapcraft
|
||||
snap/.snapcraft/
|
||||
|
|
42
.gitpod.yml
42
.gitpod.yml
|
@ -1,42 +0,0 @@
|
|||
tasks:
|
||||
- name: Setup
|
||||
init: |
|
||||
cp -r contrib/ide/vscode .vscode
|
||||
make deps
|
||||
make build
|
||||
command: |
|
||||
gp sync-done setup
|
||||
exit 0
|
||||
- name: Run frontend
|
||||
command: |
|
||||
gp sync-await setup
|
||||
make watch-frontend
|
||||
- name: Run backend
|
||||
command: |
|
||||
gp sync-await setup
|
||||
mkdir -p custom/conf/
|
||||
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
|
||||
export TAGS="sqlite sqlite_unlock_notify"
|
||||
make watch-backend
|
||||
- name: Run docs
|
||||
before: sudo bash -c "$(grep 'https://github.com/gohugoio/hugo/releases/download' Makefile | tr -d '\')" # install hugo
|
||||
command: cd docs && make clean update && hugo server -D -F --baseUrl $(gp url 1313) --liveReloadPort=443 --appendPort=false --bind=0.0.0.0
|
||||
|
||||
vscode:
|
||||
extensions:
|
||||
- editorconfig.editorconfig
|
||||
- dbaeumer.vscode-eslint
|
||||
- golang.go
|
||||
- stylelint.vscode-stylelint
|
||||
- DavidAnson.vscode-markdownlint
|
||||
- johnsoncodehk.volar
|
||||
- ms-azuretools.vscode-docker
|
||||
- zixuanchen.vitest-explorer
|
||||
- alexcvzz.vscode-sqlite
|
||||
|
||||
ports:
|
||||
- name: Gitea
|
||||
port: 3000
|
||||
- name: Docs
|
||||
port: 1313
|
|
@ -12,81 +12,22 @@ linters:
|
|||
- dupl
|
||||
#- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
|
||||
- gofmt
|
||||
- misspell
|
||||
- gocritic
|
||||
- bidichk
|
||||
- ineffassign
|
||||
- revive
|
||||
- gofumpt
|
||||
- depguard
|
||||
- nakedret
|
||||
- unconvert
|
||||
- wastedassign
|
||||
- nolintlint
|
||||
- stylecheck
|
||||
enable-all: false
|
||||
disable-all: true
|
||||
fast: false
|
||||
|
||||
run:
|
||||
go: 1.19
|
||||
timeout: 10m
|
||||
skip-dirs:
|
||||
- node_modules
|
||||
- public
|
||||
- web_src
|
||||
timeout: 3m
|
||||
|
||||
linters-settings:
|
||||
stylecheck:
|
||||
checks: ["all", "-ST1005", "-ST1003"]
|
||||
nakedret:
|
||||
max-func-lines: 0
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
- ifElseChain
|
||||
- singleCaseSwitch # Every time this occurred in the code, there was no other way.
|
||||
revive:
|
||||
ignore-generated-header: false
|
||||
severity: warning
|
||||
confidence: 0.8
|
||||
errorCode: 1
|
||||
warningCode: 1
|
||||
rules:
|
||||
- name: blank-imports
|
||||
- name: context-as-argument
|
||||
- name: context-keys-type
|
||||
- name: dot-imports
|
||||
- name: error-return
|
||||
- name: error-strings
|
||||
- name: error-naming
|
||||
- name: exported
|
||||
- name: if-return
|
||||
- name: increment-decrement
|
||||
- name: var-naming
|
||||
- name: var-declaration
|
||||
- name: package-comments
|
||||
- name: range
|
||||
- name: receiver-naming
|
||||
- name: time-naming
|
||||
- name: unexported-return
|
||||
- name: indent-error-flow
|
||||
- name: errorf
|
||||
- name: duplicated-imports
|
||||
- name: modifies-value-receiver
|
||||
gofumpt:
|
||||
extra-rules: true
|
||||
lang-version: "1.19"
|
||||
depguard:
|
||||
# TODO: use depguard to replace import checks in gitea-vet
|
||||
list-type: denylist
|
||||
# Check the list against standard lib.
|
||||
include-go-root: true
|
||||
packages-with-error-message:
|
||||
- encoding/json: "use gitea's modules/json instead of encoding/json"
|
||||
- github.com/unknwon/com: "use gitea's util and replacements"
|
||||
|
||||
issues:
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
exclude-rules:
|
||||
# Exclude some linters from running on tests files.
|
||||
- path: _test\.go
|
||||
|
@ -147,6 +88,9 @@ issues:
|
|||
- path: models/issue_comment_list.go
|
||||
linters:
|
||||
- dupl
|
||||
- linters:
|
||||
- misspell
|
||||
text: '`Unknwon` is a misspelling of `Unknown`'
|
||||
- path: models/update.go
|
||||
linters:
|
||||
- unused
|
||||
|
@ -166,10 +110,4 @@ issues:
|
|||
linters:
|
||||
- staticcheck
|
||||
text: "svc.IsAnInteractiveSession is deprecated: Use IsWindowsService instead."
|
||||
- path: models/user/openid.go
|
||||
linters:
|
||||
- golint
|
||||
- path: models/user/badge.go
|
||||
linters:
|
||||
- revive
|
||||
text: "exported: type name will be used as user.UserBadge by other packages, and that stutters; consider calling this Badge"
|
||||
|
||||
|
|
7
.ignore
7
.ignore
|
@ -1,8 +1,5 @@
|
|||
*.min.css
|
||||
*.min.js
|
||||
/vendor
|
||||
/public/vendor/plugins
|
||||
/modules/options/bindata.go
|
||||
/modules/public/bindata.go
|
||||
/modules/templates/bindata.go
|
||||
/public/vendor/plugins
|
||||
/vendor
|
||||
node_modules
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
commands-show-output: false
|
||||
fenced-code-language: false
|
||||
first-line-h1: false
|
||||
header-increment: false
|
||||
line-length: {code_blocks: false, tables: false, stern: true, line_length: -1}
|
||||
no-alt-text: false
|
||||
no-bare-urls: false
|
||||
no-blanks-blockquote: false
|
||||
no-duplicate-header: {allow_different_nesting: true}
|
||||
no-emphasis-as-header: false
|
||||
no-empty-links: false
|
||||
no-hard-tabs: {code_blocks: false}
|
||||
no-inline-html: false
|
||||
no-space-in-code: false
|
||||
no-space-in-emphasis: false
|
||||
no-trailing-punctuation: false
|
||||
no-trailing-spaces: {br_spaces: 0}
|
||||
single-h1: false
|
|
@ -0,0 +1,25 @@
|
|||
ignoreGeneratedHeader = false
|
||||
severity = "warning"
|
||||
confidence = 0.8
|
||||
errorCode = 1
|
||||
warningCode = 1
|
||||
|
||||
[rule.blank-imports]
|
||||
[rule.context-as-argument]
|
||||
[rule.context-keys-type]
|
||||
[rule.dot-imports]
|
||||
[rule.error-return]
|
||||
[rule.error-strings]
|
||||
[rule.error-naming]
|
||||
[rule.exported]
|
||||
[rule.if-return]
|
||||
[rule.increment-decrement]
|
||||
[rule.var-naming]
|
||||
[rule.var-declaration]
|
||||
[rule.package-comments]
|
||||
[rule.range]
|
||||
[rule.receiver-naming]
|
||||
[rule.time-naming]
|
||||
[rule.unexported-return]
|
||||
[rule.indent-error-flow]
|
||||
[rule.errorf]
|
|
@ -1,12 +0,0 @@
|
|||
extends: [[spectral:oas, all]]
|
||||
|
||||
rules:
|
||||
info-contact: off
|
||||
oas2-api-host: off
|
||||
oas2-parameter-description: off
|
||||
oas2-schema: off
|
||||
oas2-valid-schema-example: off
|
||||
openapi-tags: off
|
||||
operation-description: off
|
||||
operation-singular-tag: off
|
||||
operation-tag-defined: off
|
|
@ -0,0 +1,15 @@
|
|||
extends: stylelint-config-standard
|
||||
|
||||
rules:
|
||||
at-rule-empty-line-before: null
|
||||
block-closing-brace-empty-line-before: null
|
||||
color-hex-length: null
|
||||
comment-empty-line-before: null
|
||||
declaration-block-single-line-max-declarations: null
|
||||
declaration-empty-line-before: null
|
||||
indentation: 2
|
||||
no-descending-specificity: null
|
||||
number-leading-zero: never
|
||||
rule-empty-line-before: null
|
||||
selector-pseudo-element-colon-notation: double
|
||||
shorthand-property-no-redundant-values: true
|
|
@ -1,44 +0,0 @@
|
|||
extends: stylelint-config-standard
|
||||
|
||||
plugins:
|
||||
- stylelint-declaration-strict-value
|
||||
|
||||
overrides:
|
||||
- files: ["**/*.less"]
|
||||
customSyntax: postcss-less
|
||||
- files: ["**/*.less"]
|
||||
rules:
|
||||
scale-unlimited/declaration-strict-value: [color, {
|
||||
ignoreValues: /^(inherit|transparent|unset|initial)$/
|
||||
}]
|
||||
- files: ["**/chroma/*", "**/codemirror/*", "**/standalone/*", "**/console/*"]
|
||||
rules:
|
||||
scale-unlimited/declaration-strict-value: null
|
||||
|
||||
rules:
|
||||
alpha-value-notation: null
|
||||
at-rule-empty-line-before: null
|
||||
block-closing-brace-empty-line-before: null
|
||||
color-function-notation: null
|
||||
color-hex-length: null
|
||||
comment-empty-line-before: null
|
||||
declaration-block-no-redundant-longhand-properties: null
|
||||
declaration-block-single-line-max-declarations: null
|
||||
declaration-empty-line-before: null
|
||||
function-no-unknown: null
|
||||
hue-degree-notation: null
|
||||
import-notation: string
|
||||
indentation: 2
|
||||
max-line-length: null
|
||||
no-descending-specificity: null
|
||||
no-invalid-position-at-import-rule: null
|
||||
number-leading-zero: never
|
||||
number-max-precision: null
|
||||
property-no-vendor-prefix: null
|
||||
rule-empty-line-before: null
|
||||
selector-class-pattern: null
|
||||
selector-id-pattern: null
|
||||
selector-pseudo-element-colon-notation: double
|
||||
shorthand-property-no-redundant-values: true
|
||||
string-quotes: null
|
||||
value-no-vendor-prefix: null
|
|
@ -1,32 +0,0 @@
|
|||
platform: linux/amd64
|
||||
|
||||
workspace:
|
||||
base: /go
|
||||
path: src/codeberg/gitea
|
||||
|
||||
pipeline:
|
||||
deps-backend:
|
||||
image: golang:1.19
|
||||
pull: true
|
||||
commands:
|
||||
- make deps-backend
|
||||
|
||||
security-check:
|
||||
image: golang:1.19
|
||||
pull: true
|
||||
commands:
|
||||
- make security-check
|
||||
|
||||
lint-backend:
|
||||
image: gitea/test_env:linux-amd64
|
||||
pull: true
|
||||
environment:
|
||||
- TAGS=bindata sqlite sqlite_unlock_notify
|
||||
- GOSUMDB=sum.golang.org
|
||||
commands:
|
||||
- make lint-backend
|
||||
|
||||
checks-backend:
|
||||
image: golang:1.19
|
||||
commands:
|
||||
- make --always-make checks-backend
|
|
@ -1,28 +0,0 @@
|
|||
platform: linux/amd64
|
||||
|
||||
depends_on:
|
||||
- testing-amd64
|
||||
|
||||
pipeline:
|
||||
fetch-tags:
|
||||
image: docker:git
|
||||
pull: true
|
||||
commands:
|
||||
- git config --add safe.directory '*'
|
||||
- git fetch --tags --force
|
||||
|
||||
publish:
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
pull: true
|
||||
settings:
|
||||
platforms: linux/amd64
|
||||
registry:
|
||||
from_secret: domain
|
||||
tag: ${CI_COMMIT_TAG##v}
|
||||
repo: ${CI_REPO_LINK##https://}
|
||||
password:
|
||||
from_secret: releaseteamtoken
|
||||
username:
|
||||
from_secret: releaseteamuser
|
||||
when:
|
||||
event: tag
|
|
@ -1,65 +0,0 @@
|
|||
platform: linux/amd64
|
||||
|
||||
depends_on:
|
||||
- testing-amd64
|
||||
|
||||
workspace:
|
||||
base: /source
|
||||
path: /
|
||||
|
||||
pipeline:
|
||||
fetch-tags:
|
||||
image: docker:git
|
||||
pull: true
|
||||
commands:
|
||||
- git config --add safe.directory '*'
|
||||
- git fetch --tags --force
|
||||
|
||||
deps-frontend:
|
||||
image: node:18
|
||||
pull: true
|
||||
commands:
|
||||
- make deps-frontend
|
||||
|
||||
deps-backend:
|
||||
image: golang:1.19
|
||||
pull: true
|
||||
commands:
|
||||
- make deps-backend
|
||||
|
||||
static:
|
||||
image: techknowlogick/xgo:go-1.19.x
|
||||
pull: true
|
||||
commands:
|
||||
- curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get -qqy install nodejs
|
||||
- export PATH=$PATH:$GOPATH/bin
|
||||
- make CI=true LINUX_ARCHS=linux/amd64,linux/arm64 release
|
||||
environment:
|
||||
TAGS: bindata sqlite sqlite_unlock_notify
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
|
||||
gpg-sign:
|
||||
image: plugins/gpgsign:1
|
||||
pull: true
|
||||
settings:
|
||||
detach_sign: true
|
||||
excludes:
|
||||
- "dist/release/*.sha256"
|
||||
files:
|
||||
- "dist/release/*"
|
||||
key:
|
||||
from_secret: releaseteamgpg
|
||||
|
||||
release:
|
||||
image: golang:1.19
|
||||
commands:
|
||||
- curl -sL https://dl.gitea.io/tea/0.9.0/tea-0.9.0-linux-amd64 > /bin/tea && chmod +x /bin/tea
|
||||
- REMOTE=$(echo $CI_REPO_LINK | sed -e 's|.*://||' -e 's|/.*||')
|
||||
- GITEA_SERVER_URL=$CI_REPO_LINK GITEA_SERVER_TOKEN=$RELEASETEAMTOKEN tea login add --name $RELEASETEAMUSER --url $REMOTE
|
||||
- ASSETS=$(ls dist/release/* | sed -e 's/^/-a /')
|
||||
- tea release create $ASSETS --tag $CI_COMMIT_TAG --title $CI_COMMIT_TAG
|
||||
when:
|
||||
event: tag
|
||||
secrets:
|
||||
- releaseteamtoken
|
||||
- releaseteamuser
|
|
@ -1,63 +0,0 @@
|
|||
platform: linux/amd64
|
||||
|
||||
depends_on:
|
||||
- compliance
|
||||
|
||||
workspace:
|
||||
base: /go
|
||||
path: src/codeberg/gitea
|
||||
|
||||
pipeline:
|
||||
fetch-tags:
|
||||
image: docker:git
|
||||
pull: true
|
||||
commands:
|
||||
- git config --add safe.directory '*'
|
||||
- git fetch --tags --force
|
||||
|
||||
deps-backend:
|
||||
image: golang:1.19
|
||||
pull: true
|
||||
commands:
|
||||
- make deps-backend
|
||||
|
||||
tag-pre-condition:
|
||||
image: drone/git
|
||||
pull: true
|
||||
commands:
|
||||
- git update-ref refs/heads/tag_test ${CI_COMMIT_SHA}
|
||||
|
||||
prepare-test-env:
|
||||
image: gitea/test_env:linux-amd64
|
||||
pull: true
|
||||
commands:
|
||||
- ./build/test-env-prepare.sh
|
||||
|
||||
build:
|
||||
image: gitea/test_env:linux-amd64
|
||||
environment:
|
||||
- GOSUMDB=sum.golang.org
|
||||
- TAGS=bindata sqlite sqlite_unlock_notify
|
||||
commands:
|
||||
- su gitea -c './build/test-env-check.sh'
|
||||
- su gitea -c 'make backend'
|
||||
|
||||
unit-test:
|
||||
image: gitea/test_env:linux-amd64
|
||||
environment:
|
||||
- TAGS=bindata sqlite sqlite_unlock_notify
|
||||
- RACE_ENABLED=true
|
||||
secrets:
|
||||
- github_read_token
|
||||
commands:
|
||||
- su gitea -c 'make unit-test-coverage test-check'
|
||||
|
||||
test-sqlite:
|
||||
image: gitea/test_env:linux-amd64
|
||||
environment:
|
||||
- USE_REPO_TEST_DIR=1
|
||||
- GOPROXY=off
|
||||
- TAGS=bindata gogit sqlite sqlite_unlock_notify
|
||||
- TEST_TAGS=bindata gogit sqlite sqlite_unlock_notify
|
||||
commands:
|
||||
- su gitea -c 'timeout -s ABRT 120m make test-sqlite-migration test-sqlite'
|
1052
CHANGELOG.md
1052
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
366
CONTRIBUTING.md
366
CONTRIBUTING.md
|
@ -1,23 +1,361 @@
|
|||
# Forgejo Contributor Guide
|
||||
# Contribution Guidelines
|
||||
|
||||
The Forgejo project is run by a community of people who are expected to follow this guide when cooperating on a simple bug fix as well as when changing the governance. For more information about the project, take a look at [the documentation explaining what Forgejo provides](README.md).
|
||||
## Table of Contents
|
||||
|
||||
Sensitive security-related issues should be reported to [security@forgejo.org](mailto:security@forgejo.org) using [encryption](https://keyoxide.org/security@forgejo.org).
|
||||
- [Contribution Guidelines](#contribution-guidelines)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Introduction](#introduction)
|
||||
- [Bug reports](#bug-reports)
|
||||
- [Discuss your design](#discuss-your-design)
|
||||
- [Testing redux](#testing-redux)
|
||||
- [Vendoring](#vendoring)
|
||||
- [Translation](#translation)
|
||||
- [Building Gitea](#building-gitea)
|
||||
- [Code review](#code-review)
|
||||
- [Styleguide](#styleguide)
|
||||
- [Design guideline](#design-guideline)
|
||||
- [API v1](#api-v1)
|
||||
- [Developer Certificate of Origin (DCO)](#developer-certificate-of-origin-dco)
|
||||
- [Release Cycle](#release-cycle)
|
||||
- [Maintainers](#maintainers)
|
||||
- [Owners](#owners)
|
||||
- [Versions](#versions)
|
||||
- [Releasing Gitea](#releasing-gitea)
|
||||
- [Copyright](#copyright)
|
||||
|
||||
## For everyone involved
|
||||
## Introduction
|
||||
|
||||
- [Code of Conduct](CONTRIBUTING/COC.md)
|
||||
- [Bugs, features, security and others discussions](CONTRIBUTING/DISCUSSIONS.md)
|
||||
- [Governance](CONTRIBUTING/GOVERNANCE.md)
|
||||
- [Funding](CONTRIBUTING/FUNDING.md)
|
||||
This document explains how to contribute changes to the Gitea project.
|
||||
It assumes you have followed the
|
||||
[installation instructions](https://docs.gitea.io/en-us/).
|
||||
Sensitive security-related issues should be reported to
|
||||
[security@gitea.io](mailto:security@gitea.io).
|
||||
|
||||
## For contributors
|
||||
For configuring IDE or code editor to develop Gitea see [IDE and code editor configuration](contrib/ide/)
|
||||
|
||||
- [Developer Certificate of Origin (DCO)](CONTRIBUTING/DCO.md)
|
||||
- [Development workflow](CONTRIBUTING/WORKFLOW.md)
|
||||
## Bug reports
|
||||
|
||||
## For maintainers
|
||||
Please search the issues on the issue tracker with a variety of keywords
|
||||
to ensure your bug is not already reported.
|
||||
|
||||
- [Release management](CONTRIBUTING/RELEASE.md)
|
||||
- [Secrets](CONTRIBUTING/SECRETS.md)
|
||||
If unique, [open an issue](https://github.com/go-gitea/gitea/issues/new)
|
||||
and answer the questions so we can understand and reproduce the
|
||||
problematic behavior.
|
||||
|
||||
To show us that the issue you are having is in Gitea itself, please
|
||||
write clear, concise instructions so we can reproduce the behavior—
|
||||
even if it seems obvious. The more detailed and specific you are,
|
||||
the faster we can fix the issue. Check out [How to Report Bugs
|
||||
Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html).
|
||||
|
||||
Please be kind, remember that Gitea comes at no cost to you, and you're
|
||||
getting free help.
|
||||
|
||||
## Discuss your design
|
||||
|
||||
The project welcomes submissions. If you want to change or add something,
|
||||
please let everyone know what you're working on—[file an issue](https://github.com/go-gitea/gitea/issues/new)!
|
||||
Significant changes must go through the change proposal process
|
||||
before they can be accepted. To create a proposal, file an issue with
|
||||
your proposed changes documented, and make sure to note in the title
|
||||
of the issue that it is a proposal.
|
||||
|
||||
This process gives everyone a chance to validate the design, helps
|
||||
prevent duplication of effort, and ensures that the idea fits inside
|
||||
the goals for the project and tools. It also checks that the design is
|
||||
sound before code is written; the code review tool is not the place for
|
||||
high-level discussions.
|
||||
|
||||
## Testing redux
|
||||
|
||||
Before submitting a pull request, run all the tests for the whole tree
|
||||
to make sure your changes don't cause regression elsewhere.
|
||||
|
||||
Here's how to run the test suite:
|
||||
|
||||
- code lint
|
||||
|
||||
| | |
|
||||
| :-------------------- | :---------------------------------------------------------------- |
|
||||
|``make lint`` | lint everything (not suggest if you only change one type code) |
|
||||
|``make lint-frontend`` | lint frontend files |
|
||||
|``make lint-backend`` | lint backend files |
|
||||
|
||||
- run test code (Suggest run in linux)
|
||||
|
||||
| | |
|
||||
| :------------------------------------- | :----------------------------------------------- |
|
||||
|``make test[\#TestSpecificName]`` | run unit test |
|
||||
|``make test-sqlite[\#TestSpecificName]``| run [integration](integrations) test for sqlite |
|
||||
|[More detail message about integrations](integrations/README.md) |
|
||||
|
||||
## Vendoring
|
||||
|
||||
We keep a cached copy of dependencies within the `vendor/` directory,
|
||||
managing updates via [Modules](https://golang.org/cmd/go/#hdr-Module_maintenance).
|
||||
|
||||
Pull requests should only include `vendor/` updates if they are part of
|
||||
the same change, be it a bugfix or a feature addition.
|
||||
|
||||
The `vendor/` update needs to be justified as part of the PR description,
|
||||
and must be verified by the reviewers and/or merger to always reference
|
||||
an existing upstream commit.
|
||||
|
||||
You can find more information on how to get started with it on the [Modules Wiki](https://github.com/golang/go/wiki/Modules).
|
||||
|
||||
## Translation
|
||||
|
||||
We do all translation work inside [Crowdin](https://crowdin.com/project/gitea).
|
||||
The only translation that is maintained in this git repository is
|
||||
[`en_US.ini`](https://github.com/go-gitea/gitea/blob/master/options/locale/locale_en-US.ini)
|
||||
and is synced regularly to Crowdin. Once a translation has reached
|
||||
A SATISFACTORY PERCENTAGE it will be synced back into this repo and
|
||||
included in the next released version.
|
||||
|
||||
## Building Gitea
|
||||
|
||||
See the [hacking instructions](https://docs.gitea.io/en-us/hacking-on-gitea/).
|
||||
|
||||
## Code review
|
||||
|
||||
Changes to Gitea must be reviewed before they are accepted—no matter who
|
||||
makes the change, even if they are an owner or a maintainer. We use GitHub's
|
||||
pull request workflow to do that. And, we also use [LGTM](http://lgtm.co)
|
||||
to ensure every PR is reviewed by at least 2 maintainers.
|
||||
|
||||
Please try to make your pull request easy to review for us. And, please read
|
||||
the *[How to get faster PR reviews](https://github.com/kubernetes/community/blob/261cb0fd089b64002c91e8eddceebf032462ccd6/contributors/guide/pull-requests.md#best-practices-for-faster-reviews)* guide;
|
||||
it has lots of useful tips for any project you may want to contribute.
|
||||
Some of the key points:
|
||||
|
||||
* Make small pull requests. The smaller, the faster to review and the
|
||||
more likely it will be merged soon.
|
||||
* Don't make changes unrelated to your PR. Maybe there are typos on
|
||||
some comments, maybe refactoring would be welcome on a function... but
|
||||
if that is not related to your PR, please make *another* PR for that.
|
||||
* Split big pull requests into multiple small ones. An incremental change
|
||||
will be faster to review than a huge PR.
|
||||
|
||||
## Styleguide
|
||||
|
||||
For imports you should use the following format (_without_ the comments)
|
||||
```go
|
||||
import (
|
||||
// stdlib
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
// local packages
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/sdk/gitea"
|
||||
|
||||
// external packages
|
||||
"github.com/foo/bar"
|
||||
"gopkg.io/baz.v1"
|
||||
)
|
||||
```
|
||||
|
||||
## Design guideline
|
||||
|
||||
To maintain understandable code and avoid circular dependencies it is important to have a good structure of the code. The gitea code is divided into the following parts:
|
||||
|
||||
- **integration:** Integrations tests
|
||||
- **models:** Contains the data structures used by xorm to construct database tables. It also contains supporting functions to query and update the database. Dependencies to other code in Gitea should be avoided although some modules might be needed (for example for logging).
|
||||
- **models/fixtures:** Sample model data used in integration tests.
|
||||
- **models/migrations:** Handling of database migrations between versions. PRs that changes a database structure shall also have a migration step.
|
||||
- **modules:** Different modules to handle specific functionality in Gitea.
|
||||
- **public:** Frontend files (javascript, images, css, etc.)
|
||||
- **routers:** Handling of server requests. As it uses other Gitea packages to serve the request, other packages (models, modules or services) shall not depend on routers
|
||||
- **services:** Support functions for common routing operations. Uses models and modules to handle the request.
|
||||
- **templates:** Golang templates for generating the html output.
|
||||
- **vendor:** External code that Gitea depends on.
|
||||
|
||||
## API v1
|
||||
|
||||
The API is documented by [swagger](http://try.gitea.io/api/swagger) and is based on [GitHub API v3](https://developer.github.com/v3/).
|
||||
Thus, Gitea´s API should use the same endpoints and fields as GitHub´s API as far as possible, unless there are good reasons to deviate.
|
||||
If Gitea provides functionality that GitHub does not, a new endpoint can be created.
|
||||
If information is provided by Gitea that is not provided by the GitHub API, a new field can be used that doesn't collide with any GitHub fields.
|
||||
|
||||
Updating an existing API should not remove existing fields unless there is a really good reason to do so.
|
||||
The same applies to status responses. If you notice a problem, feel free to leave a comment in the code for future refactoring to APIv2 (which is currently not planned).
|
||||
|
||||
All expected results (errors, success, fail messages) should be documented
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L319-L327)).
|
||||
|
||||
All JSON input types must be defined as a struct in [modules/structs/](modules/structs/)
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/modules/structs/issue.go#L76-L91))
|
||||
and referenced in
|
||||
[routers/api/v1/swagger/options.go](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/swagger/options.go).
|
||||
They can then be used like the following:
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L318)).
|
||||
|
||||
All JSON responses must be defined as a struct in [modules/structs/](modules/structs/)
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/modules/structs/issue.go#L36-L68))
|
||||
and referenced in its category in [routers/api/v1/swagger/](routers/api/v1/swagger/)
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/swagger/issue.go#L11-L16))
|
||||
They can be used like the following:
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L277-L279))
|
||||
|
||||
In general, HTTP methods are chosen as follows:
|
||||
* **GET** endpoints return requested object and status **OK (200)**
|
||||
* **DELETE** endpoints return status **No Content (204)**
|
||||
* **POST** endpoints return status **Created (201)**, used to **create** new objects (e.g. a User)
|
||||
* **PUT** endpoints return status **No Content (204)**, used to **add/assign** existing Objects (e.g. User) to something (e.g. Org-Team)
|
||||
* **PATCH** endpoints return changed object and status **OK (200)**, used to **edit/change** an existing object
|
||||
|
||||
|
||||
An endpoint which changes/edits an object expects all fields to be optional (except ones to identify the object, which are required).
|
||||
|
||||
|
||||
## Developer Certificate of Origin (DCO)
|
||||
|
||||
We consider the act of contributing to the code by submitting a Pull
|
||||
Request as the "Sign off" or agreement to the certifications and terms
|
||||
of the [DCO](DCO) and [MIT license](LICENSE). No further action is required.
|
||||
Additionally you could add a line at the end of your commit message.
|
||||
|
||||
```
|
||||
Signed-off-by: Joe Smith <joe.smith@email.com>
|
||||
```
|
||||
|
||||
If you set your `user.name` and `user.email` git configs, you can add the
|
||||
line to the end of your commit automatically with `git commit -s`.
|
||||
|
||||
We assume in good faith that the information you provide is legally binding.
|
||||
|
||||
## Release Cycle
|
||||
|
||||
We adopted a release schedule to streamline the process of working
|
||||
on, finishing, and issuing releases. The overall goal is to make a
|
||||
minor release every 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
|
||||
merged before feature freeze. And, during the frozen period, a corresponding
|
||||
release branch is open for fixes backported from main branch. Release candidates
|
||||
are made during this period for user testing to
|
||||
obtain a final version that is maintained in this branch. A release is
|
||||
maintained by issuing patch releases to only correct critical problems
|
||||
such as crashes or security issues.
|
||||
|
||||
Major release cycles are seasonal. They always begin on the 25th and end on
|
||||
the 24th (i.e., the 25th of December to March 24th).
|
||||
|
||||
During a development cycle, we may also publish any necessary minor releases
|
||||
for the previous version. For example, if the latest, published release is
|
||||
v1.2, then minor changes for the previous release—e.g., v1.1.0 -> v1.1.1—are
|
||||
still possible.
|
||||
|
||||
## Maintainers
|
||||
|
||||
To make sure every PR is checked, we have [team
|
||||
maintainers](MAINTAINERS). Every PR **MUST** be reviewed by at least
|
||||
two maintainers (or owners) before it can get merged. A maintainer
|
||||
should be a contributor of Gitea (or Gogs) and contributed at least
|
||||
4 accepted PRs. A contributor should apply as a maintainer in the
|
||||
[Discord](https://discord.gg/NsatcWJ) #develop channel. The owners
|
||||
or the team maintainers may invite the contributor. A maintainer
|
||||
should spend some time on code reviews. If a maintainer has no
|
||||
time to do that, they should apply to leave the maintainers team
|
||||
and we will give them the honor of being a member of the [advisors
|
||||
team](https://github.com/orgs/go-gitea/teams/advisors). Of course, if
|
||||
an advisor has time to code review, we will gladly welcome them back
|
||||
to the maintainers team. If a maintainer is inactive for more than 3
|
||||
months and forgets to leave the maintainers team, the owners may move
|
||||
him or her from the maintainers team to the advisors team.
|
||||
For security reasons, Maintainers should use 2FA for their accounts and
|
||||
if possible provide gpg signed commits.
|
||||
https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/
|
||||
https://help.github.com/articles/signing-commits-with-gpg/
|
||||
|
||||
## Owners
|
||||
|
||||
Since Gitea is a pure community organization without any company support,
|
||||
to keep the development healthy we will elect three owners every year. All
|
||||
contributors may vote to elect up to three candidates, one of which will
|
||||
be the main owner, and the other two the assistant owners. When the new
|
||||
owners have been elected, the old owners will give up ownership to the
|
||||
newly elected owners. If an owner is unable to do so, the other owners
|
||||
will assist in ceding ownership to the newly elected owners.
|
||||
For security reasons, Owners or any account with write access (like a bot)
|
||||
must use 2FA.
|
||||
https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/
|
||||
|
||||
After the election, the new owners should proactively agree
|
||||
with our [CONTRIBUTING](CONTRIBUTING.md) requirements in the
|
||||
[Discord](https://discord.gg/NsatcWJ) #general channel. Below are the
|
||||
words to speak:
|
||||
|
||||
```
|
||||
I'm honored to having been elected an owner of Gitea, I agree with
|
||||
[CONTRIBUTING](CONTRIBUTING.md). I will spend part of my time on Gitea
|
||||
and lead the development of Gitea.
|
||||
```
|
||||
|
||||
To honor the past owners, here's the history of the owners and the time
|
||||
they served:
|
||||
|
||||
* 2021-01-01 ~ 2021-12-31 - https://github.com/go-gitea/gitea/issues/13801
|
||||
* [Lunny Xiao](https://gitea.com/lunny) <xiaolunwen@gmail.com>
|
||||
* [Lauris Bukšis-Haberkorns](https://gitea.com/lafriks) <lauris@nix.lv>
|
||||
* [Matti Ranta](https://gitea.com/techknowlogick) <techknowlogick@gitea.io>
|
||||
|
||||
* 2020-01-01 ~ 2020-12-31 - https://github.com/go-gitea/gitea/issues/9230
|
||||
* [Lunny Xiao](https://gitea.com/lunny) <xiaolunwen@gmail.com>
|
||||
* [Lauris Bukšis-Haberkorns](https://gitea.com/lafriks) <lauris@nix.lv>
|
||||
* [Matti Ranta](https://gitea.com/techknowlogick) <techknowlogick@gitea.io>
|
||||
|
||||
* 2019-01-01 ~ 2019-12-31 - https://github.com/go-gitea/gitea/issues/5572
|
||||
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
||||
* [Lauris Bukšis-Haberkorns](https://github.com/lafriks) <lauris@nix.lv>
|
||||
* [Matti Ranta](https://github.com/techknowlogick) <techknowlogick@gitea.io>
|
||||
|
||||
* 2018-01-01 ~ 2018-12-31 - https://github.com/go-gitea/gitea/issues/3255
|
||||
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
||||
* [Lauris Bukšis-Haberkorns](https://github.com/lafriks) <lauris@nix.lv>
|
||||
* [Kim Carlbäcker](https://github.com/bkcsoft) <kim.carlbacker@gmail.com>
|
||||
|
||||
* 2016-11-04 ~ 2017-12-31
|
||||
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
||||
* [Thomas Boerger](https://github.com/tboerger) <thomas@webhippie.de>
|
||||
* [Kim Carlbäcker](https://github.com/bkcsoft) <kim.carlbacker@gmail.com>
|
||||
|
||||
## Versions
|
||||
|
||||
Gitea has the `master` branch as a tip branch and has version branches
|
||||
such as `release/v0.9`. `release/v0.9` is a release branch and we will
|
||||
tag `v0.9.0` for binary download. If `v0.9.0` has bugs, we will accept
|
||||
pull requests on the `release/v0.9` branch and publish a `v0.9.1` tag,
|
||||
after bringing the bug fix also to the master branch.
|
||||
|
||||
Since the `master` branch is a tip version, if you wish to use Gitea
|
||||
in production, please download the latest release tag version. All the
|
||||
branches will be protected via GitHub, all the PRs to every branch must
|
||||
be reviewed by two maintainers and must pass the automatic tests.
|
||||
|
||||
## Releasing Gitea
|
||||
|
||||
* Let $vmaj, $vmin and $vpat be Major, Minor and Patch version numbers, $vpat should be rc1, rc2, 0, 1, ...... $vmaj.$vmin will be kept the same as milestones on github or gitea in future.
|
||||
* Before releasing, confirm all the version's milestone issues or PRs has been resolved. Then discuss the release on discord channel #maintainers and get agreed with almost all the owners and mergers. Or you can declare the version and if nobody against in about serval hours.
|
||||
* If this is a big version first you have to create PR for changelog on branch `master` with PRs with label `changelog` and after it has been merged do following steps:
|
||||
* Create `-dev` tag as `git tag -s -F release.notes v$vmaj.$vmin.0-dev` and push the tag as `git push origin v$vmaj.$vmin.0-dev`.
|
||||
* When CI has finished building tag then you have to create a new branch named `release/v$vmaj.$vmin`
|
||||
* If it is bugfix version create PR for changelog on branch `release/v$vmaj.$vmin` and wait till it is reviewed and merged.
|
||||
* Add a tag as `git tag -s -F release.notes v$vmaj.$vmin.$`, release.notes file could be a temporary file to only include the changelog this version which you added to `CHANGELOG.md`.
|
||||
* And then push the tag as `git push origin v$vmaj.$vmin.$`. Drone CI will automatically created a release and upload all the compiled binary. (But currently it didn't add the release notes automatically. Maybe we should fix that.)
|
||||
* If needed send PR for changelog on branch `master`.
|
||||
* Send PR to [blog repository](https://gitea.com/gitea/blog) announcing the release.
|
||||
|
||||
## Copyright
|
||||
|
||||
Code that you contribute should use the standard copyright header:
|
||||
|
||||
```
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
```
|
||||
|
||||
Files in the repository contain copyright from the year they are added
|
||||
to the year they are last changed. If the copyright author is changed,
|
||||
just paste the header below the old one.
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
# Code of Conduct, Well Being and Moderation teams
|
||||
|
||||
Forgejo strives to be an inclusive project where everyone can participate in a safe environment. The [Well Being](https://codeberg.org/org/forgejo/teams/well-being) team is doing its best to defuse tensions before they escalate and is available to answer all requests sent its way. When diplomacy fails, the [Moderation](https://codeberg.org/org/forgejo/teams/moderation) will be forced to act to put a stop to actions that are contrary to the [Code of Conduct](https://codeberg.org/forgejo/code-of-conduct).
|
||||
|
||||
## Well Being and Moderation teams
|
||||
|
||||
Temporary Well Being and Moderation teams [were appointed 10 November 2022](https://codeberg.org/forgejo/meta/issues/13).
|
||||
|
||||
The moderation team will rely on this [Code of Conduct](https://codeberg.org/forgejo/code-of-conduct) when diplomacy fails.
|
||||
|
||||
### [Well Being](https://codeberg.org/org/forgejo/teams/well-being)
|
||||
|
||||
Their goal is to defuse tensions.
|
||||
|
||||
It has no power whatsover. The members are approved by the organization and trusted to:
|
||||
|
||||
- Read all communications to detect tensions between people before they escalate.
|
||||
- Do their best to defuse tensions.
|
||||
|
||||
### [Moderation](https://codeberg.org/org/forgejo/teams/moderation)
|
||||
|
||||
Their goal is to enforce the [Code of Conduct](https://codeberg.org/forgejo/code-of-conduct) when diplomacy fails.
|
||||
|
||||
It has the power to exclude people from a space.
|
||||
|
||||
Their decisions must be logical, fact based and transparent to the organization trusting them with the task.
|
|
@ -1,29 +0,0 @@
|
|||
# Developer Certificate of Origin (DCO)
|
||||
|
||||
Contributions to Forgejo, in all the repositories in the [Forgejo organization](https://codeberg.org/forgejo) are accepted provided the author agrees to the following Developer Certificate of Origin (DCO).
|
||||
|
||||
```
|
||||
By making a contribution to Forgejo, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the Free Software
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate Free Software
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same Free Software license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the Free Software license(s) involved.
|
||||
```
|
|
@ -1,18 +0,0 @@
|
|||
# Bugs, features and discussions
|
||||
|
||||
The [Forgejo issue tracker](https://codeberg.org/forgejo/forgejo/issues) is where **bugs** should be reported and **features** requested.
|
||||
|
||||
Dedicated repositories in the [Forgejo organization](https://codeberg.org/forgejo) cover areas such as:
|
||||
- the [website](https://codeberg.org/forgejo/website)
|
||||
- the [Code of Conduct](https://codeberg.org/forgejo/code-of-conduct)
|
||||
- the [funding](https://codeberg.org/forgejo/funding).
|
||||
|
||||
Other discussions regarding all **non technical aspects** of Forgejo, such as the governance, happen in the [meta issue tracker](https://codeberg.org/forgejo/meta/issues) and in the [matrix chatroom](https://matrix.to/#/#forgejo-chat:matrix.org).
|
||||
|
||||
# Security
|
||||
|
||||
The [security team](https://codeberg.org/org/forgejo/teams/security) handle security vulnerabilities. It handles sensitive security-related issues reported to [security@forgejo.org](mailto:security@forgejo.org) using [encryption](https://keyoxide.org/security@forgejo.org).
|
||||
|
||||
The security team also keeps the content of the [security.txt](https://codeberg.org/forgejo/website/src/branch/main/public/.well-known/security.txt) file up to date.
|
||||
|
||||
The private GPG key for `security@forgejo.org` is shared among all members of the security team and not stored online.
|
|
@ -1,21 +0,0 @@
|
|||
# Funding per year
|
||||
|
||||
## 2022
|
||||
|
||||
* 50,000€ [to further forge federation](https://forum.forgefriends.org/t/nlnet-grant-application-for-federation-in-gitea-deadline-august-1st-2022/823)
|
||||
* 10,000€ (1,400€ October, 5,600€ November, 2,800€ December) employee delegation from Easter-eggs (Loïc Dachary)
|
||||
|
||||
# Funding ideas and prospects
|
||||
|
||||
## Grant applications
|
||||
|
||||
* [Forgejo NLnet December 1st 2022](https://codeberg.org/forgejo/funding/issues/1)
|
||||
* [UX/UI NLnet December 1st 2022](https://codeberg.org/forgejo/funding/issues/4)
|
||||
|
||||
## Discussions
|
||||
|
||||
* [Proposition](https://codeberg.org/forgejo/meta/issues/51) to log volunteers time so that it is accounted for at a 60€ per hour rate.
|
||||
* [A solution for sustaining Free Software forge development](https://blog.dachary.org/2022/11/05/a-solution-for-sustaining-forge-development/)
|
||||
* A French company delegates an employee for X month
|
||||
* The employee's time is paid for by [R&D incentive](https://fr.wikipedia.org/wiki/Cr%C3%A9dit_d%27imp%C3%B4t_recherche)
|
||||
* [Donations to Codeberg](https://codeberg.org/forgejo/meta/issues/26)
|
|
@ -1,21 +0,0 @@
|
|||
# Governance
|
||||
|
||||
## Codeberg e.V. stewards of the domains
|
||||
|
||||
The Forgejo [domains](https://codeberg.org/forgejo/meta/issues/41) are owned by the democratic non-profit dedicated to Free Software [Codeberg e.V.](https://codeberg.org/Codeberg/org/src/branch/main/en/bylaws.md). Forgejo is therefore ultimately under the control of Codeberg e.V. and its governance. However, although Codeberg e.V. is committed to use and host Forgejo, it is expected that Forgejo defines its own governance, in a way that is compatible with the Codeberg e.V. governance.
|
||||
|
||||
## Interim Forgejo Governance
|
||||
|
||||
Although Codeberg e.V. guarantees Forgejo is ultimately under the control of a trusted organization, there was a need to establish an interim Forgejo governance for safeguarding credentials, enforcing the Code of Conduct and ensuring security vulnerabilities are handled responsibly for the Forgejo releases.
|
||||
|
||||
All people with a role in the interim Forgejo governance pledge to resign as soon as the Forgejo governance is in place.
|
||||
|
||||
* [release managers](https://codeberg.org/org/forgejo/teams/releases) safeguard the keys used to sign Forgejo releases
|
||||
* [secret keepers](https://codeberg.org/org/Forgejo/teams/owners) safeguard all credentials (registrar, social accounts, etc.)
|
||||
* [security team](https://codeberg.org/org/forgejo/teams/security) handle security vulnerabilities
|
||||
* [Well Being](https://codeberg.org/org/forgejo/teams/well-being) and [Moderation](https://codeberg.org/org/forgejo/teams/moderation) teams help keep Forgejo an inclusive space
|
||||
|
||||
## Forgejo Governance
|
||||
|
||||
Forgejo was bootstraped in November 2022 and is [defining its governance](https://codeberg.org/forgejo/meta/issues/19). The [first meeting happened November 24th](https://codeberg.org/forgejo/meta/issues/19#issuecomment-694460) and was recorded. Everyone is welcome to participate in this fully transparent and cooperative process.
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
# Release management
|
||||
|
||||
## Shared user: release-team
|
||||
|
||||
The [release-team](https://codeberg.org/release-team) user publishes and signs all releases. The associated email is mailto:release@forgejo.org.
|
||||
|
||||
The public GPG key used to sign the releases is [EB114F5E6C0DC2BCDD183550A4B61A2DC5923710](https://codeberg.org/release-team.gpg) `Forgejo Releases <release@forgejo.org>`
|
||||
|
||||
## Release numbering
|
||||
|
||||
The Forgejo release numbers are composed of the Gitea release number followed by a dash and a serial number. For instance:
|
||||
|
||||
* Gitea **v1.18.0** will be Forgejo **v1.18.0-0**, **v1.18.0-1**, etc
|
||||
|
||||
The Gitea release candidates are suffixed with **-rcN** which is handled as a special case for packaging: although **X.Y.Z** is lexicographically lower than **X.Y.Z-rc1** is is considered greater. The Forgejo serial number must therefore be inserted before the **-rcN** suffix to preserve the expected version ordering.
|
||||
|
||||
* Gitea **v1.18.0-rc0** will be Forgejo **v1.18.0-0-rc0**, **v1.18.0-1-rc0**
|
||||
* Gitea **v1.18.0-rc1** will be Forgejo **v1.18.0-2-rc1**, **v1.18.0-3-rc1**, **v1.18.0-4-rc1**
|
||||
* Gitea **v1.18.0** will be Forgejo **v1.18.0-5**, **v1.18.0-6**, **v1.18.0-7**
|
||||
* etc.
|
||||
|
||||
## Release process
|
||||
|
||||
* Reset the vX.Y/forgejo-integration branch to the Gitea tag vX.Y.Z
|
||||
* Merge all feature branches into the vX.Y/forgejo-integration branch
|
||||
* If the CI passes reset the vX.Y/forgejo branch to the tip of vX.Y/forgejo-integration
|
||||
* Set the vX.Y.Z-N tag to the tip of the vX.Y/forgejo branch
|
||||
* [Binaries](https://codeberg.org/forgejo/forgejo/releases) are built, signed and uploaded by the CI.
|
||||
* [Container images](https://codeberg.org/forgejo/-/packages/container/forgejo/versions) are built and uploaded by the CI.
|
||||
|
||||
## Release signing keys management
|
||||
|
||||
A GPG master key with no expiration date is created and shared with members of the Owners team via encrypted email. A subkey with a one year expiration date is created and stored in the secrets repository, to be used by the CI pipeline. The public master key is stored in the secrets repository and published where relevant.
|
||||
|
||||
### Master key creation
|
||||
|
||||
* gpg --expert --full-generate-key
|
||||
* key type: ECC and ECC option with Curve 25519 as curve
|
||||
* no expiration
|
||||
* id: Forgejo Releases <contact@forgejo.org>
|
||||
* gpg --export-secret-keys --armor EB114F5E6C0DC2BCDD183550A4B61A2DC5923710 and send via encrypted email to Owners
|
||||
* gpg --export --armor EB114F5E6C0DC2BCDD183550A4B61A2DC5923710 > release-team-gpg.pub
|
||||
* commit to the secret repository
|
||||
|
||||
### Subkey creation and renewal
|
||||
|
||||
* gpg --expert --edit-key EB114F5E6C0DC2BCDD183550A4B61A2DC5923710
|
||||
* addkey
|
||||
* key type: ECC (signature only)
|
||||
* key validity: one year
|
||||
* create [an issue](https://codeberg.org/forgejo/forgejo/issues) to schedule the renewal
|
||||
|
||||
#### 2023
|
||||
|
||||
* gpg --export --armor F7CBF02094E7665E17ED6C44E381BF3E50D53707 > 2023-release-team-gpg.pub
|
||||
* gpg --export-secret-keys --armor F7CBF02094E7665E17ED6C44E381BF3E50D53707 > 2023-release-team-gpg
|
||||
* commit to the secrets repository
|
||||
* renewal issue https://codeberg.org/forgejo/forgejo/issues/58
|
||||
|
||||
### CI configuration
|
||||
|
||||
The `releaseteamgpg` secret in the Woodpecker CI configuration is set with the subkey.
|
|
@ -1,56 +0,0 @@
|
|||
# Secrets
|
||||
|
||||
All Forgejo credentials are shared among the [secret keepers](https://codeberg.org/org/Forgejo/teams/owners) teams in a private repository with encrypted content.
|
||||
|
||||
## Get started
|
||||
|
||||
1. Make sure you have a GPG Key, or [create one](https://github.com/NicoHood/gpgit#12-key-generation)
|
||||
2. Send someone else your public key and ask this person to add yourself as a recipient
|
||||
```
|
||||
# Commands for the other person
|
||||
$ gpg --import public_key.asc
|
||||
# The following command will open a prompt, with the available public keys.
|
||||
# Choose the one you just added and all secrets will be re-encrypted with this new key.
|
||||
$ gopass recipients add
|
||||
```
|
||||
3. [Install gopass](https://www.gopass.pw/#install)
|
||||
> :warning: When installing on Ubuntu or Debian you can either download the deb package, install manually or build from source or use our APT repository ([github comment](https://github.com/gopasspw/gopass/issues/1849#issuecomment-802789285) with more information).
|
||||
4. Clone this repo using `gopass` (the name and email are for `git config`)
|
||||
```
|
||||
$ gopass clone git@codeberg.org:Forgejo/gopass.git
|
||||
```
|
||||
5. Check the consistency of the gopass storage
|
||||
```
|
||||
$ gopass fsck
|
||||
```
|
||||
|
||||
## Get a secret
|
||||
|
||||
Show the whole secret file:
|
||||
```
|
||||
$ gopass show ovh.com/manager
|
||||
```
|
||||
|
||||
Copy the password in the clipboard:
|
||||
```
|
||||
$ gopass show -c ovh.com/manager
|
||||
```
|
||||
|
||||
Copy the `user` part of the secret in the clipboard:
|
||||
```
|
||||
$ gopass show -c ovh.com/manager user
|
||||
```
|
||||
|
||||
## Insert or edit a secret
|
||||
```
|
||||
$ gopass edit ovh.com/manager
|
||||
```
|
||||
In the editor, insert the password on the first line.
|
||||
You may then add lines with a `key: value` syntax (`user: username` for instance).
|
||||
|
||||
## Debugging and manual git operations
|
||||
|
||||
The following command will show the location and status of the git repo (all git commands are available).
|
||||
```
|
||||
$ gopass git status
|
||||
```
|
|
@ -1,79 +0,0 @@
|
|||
# Development workflow
|
||||
|
||||
Forgejo is a soft fork, i.e. a set of commits applied to the Gitea development branch and the stable branches. On a regular basis those commits are rebased and modified if necessary to keep working. All Forgejo commits are merged into a branch from which binary releases and packages are created and distributed. The development workflow is a set of conventions Forgejo developers are expected to follow to work together.
|
||||
|
||||
Discussions on how the workflow should evolve happen [in the isssue tracker](https://codeberg.org/forgejo/forgejo/issues?type=all&state=open&labels=&milestone=0&assignee=0&q=%5BWORKFLOW%5D).
|
||||
|
||||
## Naming conventions
|
||||
|
||||
### Development
|
||||
|
||||
* Gitea: main
|
||||
* Forgejo: forgejo
|
||||
* Integration: forgejo-integration
|
||||
* Feature branches: forgejo-feature-name
|
||||
|
||||
### Stable
|
||||
|
||||
* Gitea: release/vX.Y
|
||||
* Forgejo: vX.Y/forgejo
|
||||
* Integration: vX.Y/forgejo-integration
|
||||
* Feature branches: vX.Y/forgejo-feature-name
|
||||
|
||||
## Rebasing
|
||||
|
||||
### *Feature branch*
|
||||
|
||||
The *Gitea* branches are mirrored with the Gitea development and stable branches.
|
||||
|
||||
On a regular basis, each *Feature branch* is rebased against the base *Gitea* branch.
|
||||
|
||||
### *Integration* and *Forgejo*
|
||||
|
||||
The latest *Gitea* branch resets the *Integration* branch and all *Feature branches* are merged into it.
|
||||
|
||||
If tests pass, the *Forgejo* branch is reset to the tip of the *Integration* branch.
|
||||
|
||||
If tests do not pass, an issue is filed to the *Feature branch* that fails the test. Once the issue is resolved, another round of rebasing starts.
|
||||
|
||||
## Releasing
|
||||
|
||||
When a tag is set to a *Stable* *Forgejo* branch, the CI pipeline creates and uploads binaries and packages.
|
||||
|
||||
## Feature branches
|
||||
|
||||
All *Feature branches* are based on the {vX.Y/,}forgejo-development branch which provides and other development tools and documenation.
|
||||
|
||||
The \*forgejo-development branch is based on the {vX.Y/,}forgejo-ci branch which provides the Woodpecker CI configuration.
|
||||
|
||||
The purpose of each *Feature branch* is documented below:
|
||||
|
||||
### General purpose
|
||||
|
||||
* [forgejo-ci](https://codeberg.org/forgejo/forgejo/src/branch/forgejo-ci) based on [main](https://codeberg.org/forgejo/forgejo/src/branch/main)
|
||||
Woodpecker CI configuration, including the release process.
|
||||
* Backports: [v1.18/forgejo-ci](https://codeberg.org/forgejo/forgejo/src/branch/v1.18/forgejo-ci)
|
||||
|
||||
* [forgejo-development](https://codeberg.org/forgejo/forgejo/src/branch/forgejo-development) based on [forgejo-ci](https://codeberg.org/forgejo/forgejo/src/branch/forgejo-ci)
|
||||
Forgejo development tools and documentation.
|
||||
* Backports: [v1.18/forgejo-development](https://codeberg.org/forgejo/forgejo/src/branch/v1.18/forgejo-development)
|
||||
|
||||
### [Federation](https://codeberg.org/forgejo/forgejo/issues?labels=79349)
|
||||
|
||||
* [forgejo-federation](https://codeberg.org/forgejo/forgejo/src/branch/forgejo-federation) based on [forgejo-development](https://codeberg.org/forgejo/forgejo/src/branch/forgejo-development)
|
||||
Federation support for Forgejo
|
||||
|
||||
## Pull requests and feature branches
|
||||
|
||||
Most people who are used to contributing will be familiar with the workflow of sending a pull request against the default branch. When that happens the reviewer should change the base branch to the appropriate *Feature branch* instead. If the pull request does not fit in any *Feature branch*, the reviewer needs to make decision to either:
|
||||
|
||||
* Decline the pull request because it is best contributed to Gitea
|
||||
* Create a new *Feature branch*
|
||||
|
||||
Returning contributors can figure out which *Feature branch* to base their pull request on using the list of *Feature branches*.
|
||||
|
||||
## Granularity
|
||||
|
||||
*Feature branches* can contain a number of commits grouped together, for instance for branding the documentation, the landing page and the footer. It makes it convenient for people working on that topic to get the big picture without browsing multiple branches. Creating a new *Feature branch* for each individual commit, while possible, is likely to be difficult to work with.
|
||||
|
||||
Observing the granularity of the existing *Feature branches* is the best way to figure out what works and what does not. It requires adjustments from time to time depending on the number of contributors and the complexity of the Forgejo codebase that sits on top of Gitea.
|
15
Dockerfile
15
Dockerfile
|
@ -1,5 +1,7 @@
|
|||
|
||||
###################################
|
||||
#Build stage
|
||||
FROM codeberg.org/forgejo/golang:1.19-alpine3.17 AS build-env
|
||||
FROM techknowlogick/go:1.16-alpine3.13 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY ${GOPROXY:-direct}
|
||||
|
@ -23,8 +25,8 @@ RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
|||
# Begin env-to-ini build
|
||||
RUN go build contrib/environment-to-ini/environment-to-ini.go
|
||||
|
||||
FROM codeberg.org/forgejo/alpine:3.17.0
|
||||
LABEL maintainer="contact@forgejo.org"
|
||||
FROM alpine:3.13
|
||||
LABEL maintainer="maintainers@gitea.io"
|
||||
|
||||
EXPOSE 22 3000
|
||||
|
||||
|
@ -64,9 +66,4 @@ CMD ["/bin/s6-svscan", "/etc/s6"]
|
|||
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/environment-to-ini /usr/local/bin/environment-to-ini
|
||||
#
|
||||
# s/755/775/ in the following is to avoid the corrupted layer 4f4fb700ef54
|
||||
# https://codeberg.org/Codeberg/Community/issues/800#issue-210309
|
||||
#
|
||||
RUN chmod 775 /usr/bin/entrypoint /app/gitea/gitea /usr/local/bin/gitea /usr/local/bin/environment-to-ini
|
||||
RUN chmod 775 /etc/s6/gitea/* /etc/s6/openssh/* /etc/s6/.s6-svscan/*
|
||||
RUN ln -s /app/gitea/gitea /usr/local/bin/gitea
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
|
||||
###################################
|
||||
#Build stage
|
||||
FROM golang:1.19-alpine3.17 AS build-env
|
||||
FROM techknowlogick/go:1.16-alpine3.13 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY ${GOPROXY:-direct}
|
||||
|
@ -7,7 +9,7 @@ ENV GOPROXY ${GOPROXY:-direct}
|
|||
ARG GITEA_VERSION
|
||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
||||
ENV TAGS "bindata timetzdata $TAGS"
|
||||
ARG CGO_EXTRA_CFLAGS
|
||||
ARG CGO_EXTRA_CFLAGS
|
||||
|
||||
#Build deps
|
||||
RUN apk --no-cache add build-base git nodejs npm
|
||||
|
@ -23,7 +25,7 @@ RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
|||
# Begin env-to-ini build
|
||||
RUN go build contrib/environment-to-ini/environment-to-ini.go
|
||||
|
||||
FROM alpine:3.17
|
||||
FROM alpine:3.13
|
||||
LABEL maintainer="maintainers@gitea.io"
|
||||
|
||||
EXPOSE 2222 3000
|
||||
|
@ -51,9 +53,8 @@ RUN mkdir -p /var/lib/gitea /etc/gitea
|
|||
RUN chown git:git /var/lib/gitea /etc/gitea
|
||||
|
||||
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 /usr/local/bin/gitea
|
||||
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
||||
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
|
||||
|
||||
#git:git
|
||||
USER 1000:1000
|
||||
|
@ -62,7 +63,7 @@ ENV GITEA_CUSTOM /var/lib/gitea/custom
|
|||
ENV GITEA_TEMP /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 (usefull to test and revert a config)
|
||||
ENV GITEA_APP_INI /etc/gitea/app.ini
|
||||
ENV HOME "/var/lib/gitea/git"
|
||||
VOLUME ["/var/lib/gitea", "/etc/gitea"]
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
FROM golang:1.19-alpine3.16
|
||||
|
||||
RUN pwd
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
Alexey Makhov <amakhov@avito.ru> (@makhov)
|
||||
Andrey Nering <andrey.nering@gmail.com> (@andreynering)
|
||||
Bo-Yi Wu <appleboy.tw@gmail.com> (@appleboy)
|
||||
Ethan Koenig <ethantkoenig@gmail.com> (@ethantkoenig)
|
||||
Kees de Vries <bouwko@gmail.com> (@Bwko)
|
||||
|
@ -43,9 +44,3 @@ Patrick Schratz <patrick.schratz@gmail.com> (@pat-s)
|
|||
Janis Estelmann <admin@oldschoolhack.me> (@KN4CK3R)
|
||||
Steven Kriegler <sk.bunsenbrenner@gmail.com> (@justusbunsi)
|
||||
Jimmy Praet <jimmy.praet@telenet.be> (@jpraet)
|
||||
Leon Hofmeister <dev.lh@web.de> (@delvh)
|
||||
silentcode <silentcode@senga.org> (@silentcodeg)
|
||||
Wim <wim@42.be> (@42wim)
|
||||
xinyu <xinyu@nerv.org.cn> (@penlinux)
|
||||
Jason Song <i@wolfogre.com> (@wolfogre)
|
||||
Yarden Shoham <hrsi88@gmail.com> (@yardenshoham)
|
||||
|
|
541
Makefile
541
Makefile
|
@ -17,25 +17,16 @@ else
|
|||
DIST := dist
|
||||
DIST_DIRS := $(DIST)/binaries $(DIST)/release
|
||||
IMPORT := code.gitea.io/gitea
|
||||
export GO111MODULE=on
|
||||
|
||||
GO ?= go
|
||||
SHASUM ?= shasum -a 256
|
||||
HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
|
||||
COMMA := ,
|
||||
|
||||
XGO_VERSION := go-1.19.x
|
||||
|
||||
AIR_PACKAGE ?= github.com/cosmtrek/air@v1.40.4
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.5.0
|
||||
ERRCHECK_PACKAGE ?= github.com/kisielk/errcheck@v1.6.1
|
||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.3.1
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.47.0
|
||||
GXZ_PAGAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.10
|
||||
MISSPELL_PACKAGE ?= github.com/client9/misspell/cmd/misspell@v0.3.4
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.0
|
||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.3.0
|
||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@latest
|
||||
XGO_VERSION := go-1.16.x
|
||||
MIN_GO_VERSION := 001016000
|
||||
MIN_NODE_VERSION := 012017000
|
||||
|
||||
DOCKER_IMAGE ?= gitea/gitea
|
||||
DOCKER_TAG ?= latest
|
||||
|
@ -66,9 +57,11 @@ else
|
|||
SED_INPLACE := sed -i ''
|
||||
endif
|
||||
|
||||
GOFMT ?= gofmt -s
|
||||
|
||||
EXTRA_GOFLAGS ?=
|
||||
|
||||
MAKE_VERSION := $(shell "$(MAKE)" -v | head -n 1)
|
||||
MAKE_VERSION := $(shell $(MAKE) -v | head -n 1)
|
||||
MAKE_EVIDENCE_DIR := .make_evidence
|
||||
|
||||
ifeq ($(RACE_ENABLED),true)
|
||||
|
@ -83,7 +76,7 @@ ifneq ($(DRONE_TAG),)
|
|||
GITEA_VERSION ?= $(VERSION)
|
||||
else
|
||||
ifneq ($(DRONE_BRANCH),)
|
||||
VERSION ?= $(shell echo $(DRONE_BRANCH) | sed -e 's|v\([0-9.][0-9.]*\)/.*|\1|')
|
||||
VERSION ?= $(subst release/v,,$(DRONE_BRANCH))
|
||||
else
|
||||
VERSION ?= main
|
||||
endif
|
||||
|
@ -100,8 +93,7 @@ LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(G
|
|||
|
||||
LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64
|
||||
|
||||
GO_PACKAGES ?= $(filter-out code.gitea.io/gitea/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_PACKAGES ?= $(filter-out code.gitea.io/gitea/models/migrations code.gitea.io/gitea/integrations/migration-test code.gitea.io/gitea/integrations,$(shell $(GO) list -mod=vendor ./... | grep -v /vendor/))
|
||||
|
||||
FOMANTIC_WORK_DIR := web_src/fomantic
|
||||
|
||||
|
@ -113,41 +105,31 @@ WEBPACK_DEST_ENTRIES := public/js public/css public/fonts public/img/webpack pub
|
|||
BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
|
||||
BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))
|
||||
|
||||
GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go
|
||||
|
||||
SVG_DEST_DIR := public/img/svg
|
||||
|
||||
AIR_TMP_DIR := .air
|
||||
|
||||
GO_LICENSE_TMP_DIR := .go-licenses
|
||||
GO_LICENSE_FILE := assets/go-licenses.json
|
||||
|
||||
TAGS ?=
|
||||
TAGS_SPLIT := $(subst $(COMMA), ,$(TAGS))
|
||||
TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
|
||||
|
||||
TEST_TAGS ?= sqlite sqlite_unlock_notify
|
||||
|
||||
TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMANTIC_WORK_DIR)/node_modules $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR) $(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_DIRS := cmd tests models modules routers build services tools
|
||||
WEB_DIRS := web_src/js web_src/less
|
||||
GO_DIRS := cmd integrations models modules routers build services vendor tools
|
||||
|
||||
GO_SOURCES := $(wildcard *.go)
|
||||
GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" -not -path modules/options/bindata.go -not -path modules/public/bindata.go -not -path modules/templates/bindata.go)
|
||||
GO_SOURCES += $(GENERATED_GO_DEST)
|
||||
GO_SOURCES_NO_BINDATA := $(GO_SOURCES)
|
||||
|
||||
ifeq ($(filter $(TAGS_SPLIT),bindata),bindata)
|
||||
GO_SOURCES += $(BINDATA_DEST)
|
||||
GENERATED_GO_DEST += $(BINDATA_DEST)
|
||||
endif
|
||||
|
||||
# Force installation of playwright dependencies by setting this flag
|
||||
ifdef DEPS_PLAYWRIGHT
|
||||
PLAYWRIGHT_FLAGS += --with-deps
|
||||
endif
|
||||
GO_SOURCES_OWN := $(filter-out vendor/% %/bindata.go, $(GO_SOURCES))
|
||||
|
||||
#To update swagger use: GO111MODULE=on go get -u github.com/go-swagger/go-swagger/cmd/swagger
|
||||
SWAGGER := $(GO) run -mod=vendor github.com/go-swagger/go-swagger/cmd/swagger
|
||||
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
|
||||
SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|g
|
||||
SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|"basePath": "/api/v1"|g
|
||||
|
@ -187,9 +169,6 @@ help:
|
|||
@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 " - lint lint everything"
|
||||
@echo " - lint-frontend lint frontend files"
|
||||
@echo " - lint-backend lint backend files"
|
||||
|
@ -199,7 +178,6 @@ help:
|
|||
@echo " - test test everything"
|
||||
@echo " - test-frontend test frontend files"
|
||||
@echo " - test-backend test backend files"
|
||||
@echo " - test-e2e[\#TestSpecificName] test end to end using playwright"
|
||||
@echo " - webpack build webpack files"
|
||||
@echo " - svg build svg files"
|
||||
@echo " - fomantic build fomantic files"
|
||||
|
@ -211,20 +189,18 @@ help:
|
|||
@echo " - generate-swagger generate the swagger spec from code comments"
|
||||
@echo " - swagger-validate check if the swagger spec is valid"
|
||||
@echo " - golangci-lint run golangci-lint linter"
|
||||
@echo " - go-licenses regenerate go licenses"
|
||||
@echo " - revive run revive linter"
|
||||
@echo " - misspell check for misspellings"
|
||||
@echo " - vet examines Go source code and reports suspicious constructs"
|
||||
@echo " - tidy run go mod tidy"
|
||||
@echo " - test[\#TestSpecificName] run unit test"
|
||||
@echo " - test-sqlite[\#TestSpecificName] run integration test for sqlite"
|
||||
@echo " - pr#<index> build and start gitea from a PR with integration test data loaded"
|
||||
|
||||
.PHONY: go-check
|
||||
go-check:
|
||||
$(eval MIN_GO_VERSION_STR := $(shell grep -Eo '^go\s+[0-9]+\.[0-9]+' go.mod | cut -d' ' -f2))
|
||||
$(eval MIN_GO_VERSION := $(shell printf "%03d%03d" $(shell echo '$(MIN_GO_VERSION_STR)' | tr '.' ' ')))
|
||||
$(eval GO_VERSION := $(shell printf "%03d%03d" $(shell $(GO) version | grep -Eo '[0-9]+\.[0-9]+' | tr '.' ' ');))
|
||||
$(eval GO_VERSION := $(shell printf "%03d%03d%03d" $(shell $(GO) version | grep -Eo '[0-9]+\.[0-9.]+' | tr '.' ' ');))
|
||||
@if [ "$(GO_VERSION)" -lt "$(MIN_GO_VERSION)" ]; then \
|
||||
echo "Gitea requires Go $(MIN_GO_VERSION_STR) or greater to build. You can get it at https://go.dev/dl/"; \
|
||||
echo "Gitea requires Go 1.16 or greater to build. You can get it at https://golang.org/dl/"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
|
@ -237,12 +213,11 @@ git-check:
|
|||
|
||||
.PHONY: node-check
|
||||
node-check:
|
||||
$(eval MIN_NODE_VERSION_STR := $(shell grep -Eo '"node":.*[0-9.]+"' package.json | sed -n 's/.*[^0-9.]\([0-9.]*\)"/\1/p'))
|
||||
$(eval MIN_NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell echo '$(MIN_NODE_VERSION_STR)' | tr '.' ' ')))
|
||||
$(eval NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v | cut -c2- | tr '.' ' ');))
|
||||
$(eval MIN_NODE_VER_FMT := $(shell printf "%g.%g.%g" $(shell echo $(MIN_NODE_VERSION) | grep -o ...)))
|
||||
$(eval NPM_MISSING := $(shell hash npm > /dev/null 2>&1 || echo 1))
|
||||
@if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" -o "$(NPM_MISSING)" = "1" ]; then \
|
||||
echo "Gitea requires Node.js $(MIN_NODE_VERSION_STR) or greater and npm to build. You can get it at https://nodejs.org/en/download/"; \
|
||||
echo "Gitea requires Node.js $(MIN_NODE_VER_FMT) or greater and npm to build. You can get it at https://nodejs.org/en/download/"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
|
@ -255,38 +230,20 @@ clean:
|
|||
$(GO) clean -i ./...
|
||||
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) $(BINDATA_HASH) \
|
||||
integrations*.test \
|
||||
e2e*.test \
|
||||
tests/integration/gitea-integration-pgsql/ tests/integration/gitea-integration-mysql/ tests/integration/gitea-integration-mysql8/ tests/integration/gitea-integration-sqlite/ \
|
||||
tests/integration/gitea-integration-mssql/ tests/integration/indexers-mysql/ tests/integration/indexers-mysql8/ tests/integration/indexers-pgsql tests/integration/indexers-sqlite \
|
||||
tests/integration/indexers-mssql tests/mysql.ini tests/mysql8.ini tests/pgsql.ini tests/mssql.ini man/ \
|
||||
tests/e2e/gitea-e2e-pgsql/ tests/e2e/gitea-e2e-mysql/ tests/e2e/gitea-e2e-mysql8/ tests/e2e/gitea-e2e-sqlite/ \
|
||||
tests/e2e/gitea-e2e-mssql/ tests/e2e/indexers-mysql/ tests/e2e/indexers-mysql8/ tests/e2e/indexers-pgsql/ tests/e2e/indexers-sqlite/ \
|
||||
tests/e2e/indexers-mssql/ tests/e2e/reports/ tests/e2e/test-artifacts/ tests/e2e/test-snapshots/
|
||||
integrations/gitea-integration-pgsql/ integrations/gitea-integration-mysql/ integrations/gitea-integration-mysql8/ integrations/gitea-integration-sqlite/ \
|
||||
integrations/gitea-integration-mssql/ integrations/indexers-mysql/ integrations/indexers-mysql8/ integrations/indexers-pgsql integrations/indexers-sqlite \
|
||||
integrations/indexers-mssql integrations/mysql.ini integrations/mysql8.ini integrations/pgsql.ini integrations/mssql.ini man/
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
|
||||
$(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl'))
|
||||
@# strip whitespace after '{{' and before `}}` unless there is only whitespace before it
|
||||
@$(SED_INPLACE) -e 's/{{[ ]\{1,\}/{{/g' -e '/^[ ]\{1,\}}}/! s/[ ]\{1,\}}}/}}/g' $(TEMPLATES)
|
||||
|
||||
.PHONY: fmt-check
|
||||
fmt-check: fmt
|
||||
@diff=$$(git diff $(GO_SOURCES) templates $(WEB_DIRS)); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make fmt' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: misspell-check
|
||||
misspell-check:
|
||||
go run $(MISSPELL_PACKAGE) -error $(GO_DIRS) $(WEB_DIRS)
|
||||
@echo "Running go fmt..."
|
||||
@$(GOFMT) -w $(GO_SOURCES_OWN)
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
@echo "Running go vet..."
|
||||
@GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet
|
||||
@$(GO) vet $(GO_PACKAGES)
|
||||
@GOOS= GOARCH= $(GO) build -mod=vendor code.gitea.io/gitea-vet
|
||||
@$(GO) vet -vettool=gitea-vet $(GO_PACKAGES)
|
||||
|
||||
.PHONY: $(TAGS_EVIDENCE)
|
||||
|
@ -299,10 +256,8 @@ TAGS_PREREQ := $(TAGS_EVIDENCE)
|
|||
endif
|
||||
|
||||
.PHONY: generate-swagger
|
||||
generate-swagger: $(SWAGGER_SPEC)
|
||||
|
||||
$(SWAGGER_SPEC): $(GO_SOURCES_NO_BINDATA)
|
||||
$(GO) run $(SWAGGER_PACKAGE) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
|
||||
generate-swagger:
|
||||
$(SWAGGER) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
|
||||
|
||||
|
@ -318,35 +273,70 @@ swagger-check: generate-swagger
|
|||
.PHONY: swagger-validate
|
||||
swagger-validate:
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)'
|
||||
$(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)'
|
||||
$(SWAGGER) validate './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||
|
||||
.PHONY: errcheck
|
||||
errcheck:
|
||||
@hash errcheck > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u github.com/kisielk/errcheck; \
|
||||
fi
|
||||
@echo "Running errcheck..."
|
||||
$(GO) run $(ERRCHECK_PACKAGE) $(GO_PACKAGES)
|
||||
@errcheck $(GO_PACKAGES)
|
||||
|
||||
.PHONY: revive
|
||||
revive:
|
||||
@hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u github.com/mgechev/revive; \
|
||||
fi
|
||||
@revive -config .revive.toml -exclude=./vendor/... ./...
|
||||
|
||||
.PHONY: misspell-check
|
||||
misspell-check:
|
||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
||||
fi
|
||||
@echo "Running misspell-check..."
|
||||
@misspell -error -i unknwon $(GO_SOURCES_OWN)
|
||||
|
||||
.PHONY: misspell
|
||||
misspell:
|
||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
||||
fi
|
||||
@echo "Running go misspell..."
|
||||
@misspell -w -i unknwon $(GO_SOURCES_OWN)
|
||||
|
||||
.PHONY: fmt-check
|
||||
fmt-check:
|
||||
# get all go files and run go fmt on them
|
||||
@diff=$$($(GOFMT) -d $(GO_SOURCES_OWN)); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make fmt' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: checks
|
||||
checks: checks-frontend checks-backend
|
||||
|
||||
.PHONY: checks-frontend
|
||||
checks-frontend: lockfile-check svg-check
|
||||
checks-frontend: svg-check
|
||||
|
||||
.PHONY: checks-backend
|
||||
checks-backend: tidy-check swagger-check fmt-check misspell-check swagger-validate security-check
|
||||
checks-backend: misspell-check test-vendor swagger-check swagger-validate
|
||||
|
||||
.PHONY: lint
|
||||
lint: lint-frontend lint-backend
|
||||
|
||||
.PHONY: lint-frontend
|
||||
lint-frontend: node_modules
|
||||
npx eslint --color --max-warnings=0 --ext js,vue web_src/js build *.config.js docs/assets/js tests/e2e
|
||||
npx eslint --color --max-warnings=0 web_src/js build templates *.config.js
|
||||
npx stylelint --color --max-warnings=0 web_src/less
|
||||
npx spectral lint -q -F hint $(SWAGGER_SPEC)
|
||||
npx markdownlint docs *.md
|
||||
npx editorconfig-checker templates
|
||||
|
||||
.PHONY: lint-backend
|
||||
lint-backend: golangci-lint vet editorconfig-checker
|
||||
lint-backend: golangci-lint revive vet
|
||||
|
||||
.PHONY: watch
|
||||
watch:
|
||||
|
@ -359,19 +349,22 @@ watch-frontend: node-check node_modules
|
|||
|
||||
.PHONY: watch-backend
|
||||
watch-backend: go-check
|
||||
$(GO) run $(AIR_PACKAGE) -c .air.toml
|
||||
@hash air > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u github.com/cosmtrek/air; \
|
||||
fi
|
||||
air -c .air.conf
|
||||
|
||||
.PHONY: test
|
||||
test: test-frontend test-backend
|
||||
|
||||
.PHONY: test-backend
|
||||
test-backend:
|
||||
@echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
|
||||
@$(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_PACKAGES)
|
||||
@echo "Running go test with -tags '$(TEST_TAGS)'..."
|
||||
@$(GO) test $(GOTESTFLAGS) -mod=vendor -tags='$(TEST_TAGS)' $(GO_PACKAGES)
|
||||
|
||||
.PHONY: test-frontend
|
||||
test-frontend: node_modules
|
||||
npx vitest
|
||||
@NODE_OPTIONS="--experimental-vm-modules --no-warnings" npx jest --color
|
||||
|
||||
.PHONY: test-check
|
||||
test-check:
|
||||
|
@ -388,62 +381,46 @@ test-check:
|
|||
.PHONY: test\#%
|
||||
test\#%:
|
||||
@echo "Running go test with -tags '$(TEST_TAGS)'..."
|
||||
@$(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -run $(subst .,/,$*) $(GO_TEST_PACKAGES)
|
||||
@$(GO) test -mod=vendor $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -run $(subst .,/,$*) $(GO_PACKAGES)
|
||||
|
||||
.PHONY: coverage
|
||||
coverage:
|
||||
grep '^\(mode: .*\)\|\(.*:[0-9]\+\.[0-9]\+,[0-9]\+\.[0-9]\+ [0-9]\+ [0-9]\+\)$$' coverage.out > coverage-bodged.out
|
||||
grep '^\(mode: .*\)\|\(.*:[0-9]\+\.[0-9]\+,[0-9]\+\.[0-9]\+ [0-9]\+ [0-9]\+\)$$' integration.coverage.out > integration.coverage-bodged.out
|
||||
$(GO) run build/gocovmerge.go integration.coverage-bodged.out coverage-bodged.out > coverage.all
|
||||
GO111MODULE=on $(GO) run -mod=vendor build/gocovmerge.go integration.coverage.out $(shell find . -type f -name "coverage.out") > coverage.all
|
||||
|
||||
.PHONY: unit-test-coverage
|
||||
unit-test-coverage:
|
||||
@echo "Running unit-test-coverage $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
|
||||
@$(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
|
||||
@echo "Running unit-test-coverage -tags '$(TEST_TAGS)'..."
|
||||
@$(GO) test $(GOTESTFLAGS) -mod=vendor -tags='$(TEST_TAGS)' -cover -coverprofile coverage.out $(GO_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
|
||||
|
||||
.PHONY: tidy
|
||||
tidy:
|
||||
$(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)
|
||||
@$(MAKE) --no-print-directory $(GO_LICENSE_FILE)
|
||||
.PHONY: vendor
|
||||
vendor:
|
||||
$(GO) mod tidy && $(GO) mod vendor
|
||||
|
||||
vendor: go.mod go.sum
|
||||
$(GO) mod vendor
|
||||
@touch vendor
|
||||
|
||||
.PHONY: tidy-check
|
||||
tidy-check: tidy
|
||||
@diff=$$(git diff go.mod go.sum $(GO_LICENSE_FILE)); \
|
||||
.PHONY: test-vendor
|
||||
test-vendor: vendor
|
||||
@diff=$$(git diff vendor/); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make tidy' and commit the result:"; \
|
||||
echo "Please run 'make vendor' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: go-licenses
|
||||
go-licenses: $(GO_LICENSE_FILE)
|
||||
|
||||
$(GO_LICENSE_FILE): go.mod go.sum
|
||||
-$(GO) run $(GO_LICENSES_PACKAGE) 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)
|
||||
@rm -rf $(GO_LICENSE_TMP_DIR)
|
||||
|
||||
generate-ini-sqlite:
|
||||
sed -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/sqlite.ini.tmpl > tests/sqlite.ini
|
||||
integrations/sqlite.ini.tmpl > integrations/sqlite.ini
|
||||
|
||||
.PHONY: test-sqlite
|
||||
test-sqlite: integrations.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./integrations.sqlite.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test
|
||||
|
||||
.PHONY: test-sqlite\#%
|
||||
test-sqlite\#%: integrations.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./integrations.sqlite.test -test.run $(subst .,/,$*)
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-sqlite-migration
|
||||
test-sqlite-migration: migrations.sqlite.test migrations.individual.sqlite.test
|
||||
test-sqlite-migration: migrations.sqlite.test migrations.individual.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./migrations.sqlite.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./migrations.individual.sqlite.test
|
||||
|
||||
generate-ini-mysql:
|
||||
sed -e 's|{{TEST_MYSQL_HOST}}|${TEST_MYSQL_HOST}|g' \
|
||||
|
@ -451,20 +428,20 @@ generate-ini-mysql:
|
|||
-e 's|{{TEST_MYSQL_USERNAME}}|${TEST_MYSQL_USERNAME}|g' \
|
||||
-e 's|{{TEST_MYSQL_PASSWORD}}|${TEST_MYSQL_PASSWORD}|g' \
|
||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
||||
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
|
||||
tests/mysql.ini.tmpl > tests/mysql.ini
|
||||
integrations/mysql.ini.tmpl > integrations/mysql.ini
|
||||
|
||||
.PHONY: test-mysql
|
||||
test-mysql: integrations.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./integrations.mysql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test
|
||||
|
||||
.PHONY: test-mysql\#%
|
||||
test-mysql\#%: integrations.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./integrations.mysql.test -test.run $(subst .,/,$*)
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test -test.run $(subst .,/,$*)
|
||||
|
||||
.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-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./migrations.mysql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./migrations.individual.mysql.test
|
||||
|
||||
generate-ini-mysql8:
|
||||
sed -e 's|{{TEST_MYSQL8_HOST}}|${TEST_MYSQL8_HOST}|g' \
|
||||
|
@ -472,20 +449,20 @@ generate-ini-mysql8:
|
|||
-e 's|{{TEST_MYSQL8_USERNAME}}|${TEST_MYSQL8_USERNAME}|g' \
|
||||
-e 's|{{TEST_MYSQL8_PASSWORD}}|${TEST_MYSQL8_PASSWORD}|g' \
|
||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||
-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
|
||||
integrations/mysql8.ini.tmpl > integrations/mysql8.ini
|
||||
|
||||
.PHONY: test-mysql8
|
||||
test-mysql8: integrations.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql8.ini ./integrations.mysql8.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./integrations.mysql8.test
|
||||
|
||||
.PHONY: test-mysql8\#%
|
||||
test-mysql8\#%: integrations.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql8.ini ./integrations.mysql8.test -test.run $(subst .,/,$*)
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./integrations.mysql8.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-mysql8-migration
|
||||
test-mysql8-migration: migrations.mysql8.test migrations.individual.mysql8.test
|
||||
test-mysql8-migration: migrations.mysql8.test migrations.individual.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./migrations.mysql8.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./migrations.individual.mysql8.test
|
||||
|
||||
generate-ini-pgsql:
|
||||
sed -e 's|{{TEST_PGSQL_HOST}}|${TEST_PGSQL_HOST}|g' \
|
||||
|
@ -494,20 +471,20 @@ generate-ini-pgsql:
|
|||
-e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \
|
||||
-e 's|{{TEST_PGSQL_SCHEMA}}|${TEST_PGSQL_SCHEMA}|g' \
|
||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
||||
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
|
||||
tests/pgsql.ini.tmpl > tests/pgsql.ini
|
||||
integrations/pgsql.ini.tmpl > integrations/pgsql.ini
|
||||
|
||||
.PHONY: test-pgsql
|
||||
test-pgsql: integrations.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./integrations.pgsql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test
|
||||
|
||||
.PHONY: test-pgsql\#%
|
||||
test-pgsql\#%: integrations.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./integrations.pgsql.test -test.run $(subst .,/,$*)
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-pgsql-migration
|
||||
test-pgsql-migration: migrations.pgsql.test migrations.individual.pgsql.test
|
||||
test-pgsql-migration: migrations.pgsql.test migrations.individual.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./migrations.pgsql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./migrations.individual.pgsql.test
|
||||
|
||||
generate-ini-mssql:
|
||||
sed -e 's|{{TEST_MSSQL_HOST}}|${TEST_MSSQL_HOST}|g' \
|
||||
|
@ -515,205 +492,98 @@ generate-ini-mssql:
|
|||
-e 's|{{TEST_MSSQL_USERNAME}}|${TEST_MSSQL_USERNAME}|g' \
|
||||
-e 's|{{TEST_MSSQL_PASSWORD}}|${TEST_MSSQL_PASSWORD}|g' \
|
||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
|
||||
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
|
||||
tests/mssql.ini.tmpl > tests/mssql.ini
|
||||
integrations/mssql.ini.tmpl > integrations/mssql.ini
|
||||
|
||||
.PHONY: test-mssql
|
||||
test-mssql: integrations.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./integrations.mssql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test
|
||||
|
||||
.PHONY: test-mssql\#%
|
||||
test-mssql\#%: integrations.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./integrations.mssql.test -test.run $(subst .,/,$*)
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-mssql-migration
|
||||
test-mssql-migration: migrations.mssql.test migrations.individual.mssql.test
|
||||
|
||||
.PHONY: playwright
|
||||
playwright: $(PLAYWRIGHT_DIR)
|
||||
npm install --no-save @playwright/test
|
||||
npx playwright install $(PLAYWRIGHT_FLAGS)
|
||||
|
||||
.PHONY: test-e2e%
|
||||
test-e2e%: TEST_TYPE ?= e2e
|
||||
# Clear display env variable. Otherwise, chromium tests can fail.
|
||||
DISPLAY=
|
||||
|
||||
.PHONY: test-e2e
|
||||
test-e2e: test-e2e-sqlite
|
||||
|
||||
.PHONY: test-e2e-sqlite
|
||||
test-e2e-sqlite: playwright e2e.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./e2e.sqlite.test
|
||||
|
||||
.PHONY: test-e2e-sqlite\#%
|
||||
test-e2e-sqlite\#%: playwright e2e.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./e2e.sqlite.test -test.run TestE2e/$*
|
||||
|
||||
.PHONY: test-e2e-mysql
|
||||
test-e2e-mysql: playwright e2e.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./e2e.mysql.test
|
||||
|
||||
.PHONY: test-e2e-mysql\#%
|
||||
test-e2e-mysql\#%: playwright e2e.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./e2e.mysql.test -test.run TestE2e/$*
|
||||
|
||||
.PHONY: test-e2e-mysql8
|
||||
test-e2e-mysql8: playwright e2e.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql8.ini ./e2e.mysql8.test
|
||||
|
||||
.PHONY: test-e2e-mysql8\#%
|
||||
test-e2e-mysql8\#%: playwright e2e.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql8.ini ./e2e.mysql8.test -test.run TestE2e/$*
|
||||
|
||||
.PHONY: test-e2e-pgsql
|
||||
test-e2e-pgsql: playwright e2e.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./e2e.pgsql.test
|
||||
|
||||
.PHONY: test-e2e-pgsql\#%
|
||||
test-e2e-pgsql\#%: playwright e2e.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./e2e.pgsql.test -test.run TestE2e/$*
|
||||
|
||||
.PHONY: test-e2e-mssql
|
||||
test-e2e-mssql: playwright e2e.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./e2e.mssql.test
|
||||
|
||||
.PHONY: test-e2e-mssql\#%
|
||||
test-e2e-mssql\#%: playwright e2e.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./e2e.mssql.test -test.run TestE2e/$*
|
||||
test-mssql-migration: migrations.mssql.test migrations.individual.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./migrations.mssql.test -test.failfast
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./migrations.individual.mssql.test -test.failfast
|
||||
|
||||
.PHONY: bench-sqlite
|
||||
bench-sqlite: integrations.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./integrations.sqlite.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
.PHONY: bench-mysql
|
||||
bench-mysql: integrations.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./integrations.mysql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
.PHONY: bench-mssql
|
||||
bench-mssql: integrations.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./integrations.mssql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
.PHONY: bench-pgsql
|
||||
bench-pgsql: integrations.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./integrations.pgsql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
.PHONY: integration-test-coverage
|
||||
integration-test-coverage: integrations.cover.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./integrations.cover.test -test.coverprofile=integration.coverage.out
|
||||
|
||||
.PHONY: integration-test-coverage-sqlite
|
||||
integration-test-coverage-sqlite: integrations.cover.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./integrations.cover.sqlite.test -test.coverprofile=integration.coverage.out
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.cover.test -test.coverprofile=integration.coverage.out
|
||||
|
||||
integrations.mysql.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.mysql.test
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -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
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.mysql8.test
|
||||
|
||||
integrations.pgsql.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.pgsql.test
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.pgsql.test
|
||||
|
||||
integrations.mssql.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.mssql.test
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.mssql.test
|
||||
|
||||
integrations.sqlite.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -o integrations.sqlite.test -tags '$(TEST_TAGS)'
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.sqlite.test -tags '$(TEST_TAGS)'
|
||||
|
||||
integrations.cover.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -coverpkg $(shell echo $(GO_TEST_PACKAGES) | tr ' ' ',') -o integrations.cover.test
|
||||
|
||||
integrations.cover.sqlite.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration -coverpkg $(shell echo $(GO_TEST_PACKAGES) | tr ' ' ',') -o integrations.cover.sqlite.test -tags '$(TEST_TAGS)'
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -coverpkg $(shell echo $(GO_PACKAGES) | tr ' ' ',') -o integrations.cover.test
|
||||
|
||||
.PHONY: migrations.mysql.test
|
||||
migrations.mysql.test: $(GO_SOURCES) generate-ini-mysql
|
||||
$(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
|
||||
migrations.mysql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o 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
|
||||
migrations.mysql8.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.mysql8.test
|
||||
|
||||
.PHONY: migrations.pgsql.test
|
||||
migrations.pgsql.test: $(GO_SOURCES) generate-ini-pgsql
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.pgsql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/pgsql.ini ./migrations.pgsql.test
|
||||
migrations.pgsql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.pgsql.test
|
||||
|
||||
.PHONY: migrations.mssql.test
|
||||
migrations.mssql.test: $(GO_SOURCES) generate-ini-mssql
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.mssql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini ./migrations.mssql.test
|
||||
migrations.mssql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.mssql.test
|
||||
|
||||
.PHONY: migrations.sqlite.test
|
||||
migrations.sqlite.test: $(GO_SOURCES) generate-ini-sqlite
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/integration/migration-test -o migrations.sqlite.test -tags '$(TEST_TAGS)'
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./migrations.sqlite.test
|
||||
migrations.sqlite.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.sqlite.test -tags '$(TEST_TAGS)'
|
||||
|
||||
.PHONY: migrations.individual.mysql.test
|
||||
migrations.individual.mysql.test: $(GO_SOURCES)
|
||||
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
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/models/migrations -o migrations.individual.mysql.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
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$*
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/models/migrations -o migrations.individual.mysql8.test
|
||||
|
||||
.PHONY: migrations.individual.pgsql.test
|
||||
migrations.individual.pgsql.test: $(GO_SOURCES)
|
||||
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\#%
|
||||
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/$*
|
||||
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/models/migrations -o migrations.individual.pgsql.test
|
||||
|
||||
.PHONY: migrations.individual.mssql.test
|
||||
migrations.individual.mssql.test: $(GO_SOURCES) generate-ini-mssql
|
||||
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\#%
|
||||
migrations.individual.mssql.test\#%: $(GO_SOURCES) generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mssql.ini $(GO) test $(GOTESTFLAGS) -tags '$(TEST_TAGS)' code.gitea.io/gitea/models/migrations/$*
|
||||
migrations.individual.mssql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/models/migrations -o migrations.individual.mssql.test
|
||||
|
||||
.PHONY: migrations.individual.sqlite.test
|
||||
migrations.individual.sqlite.test: $(GO_SOURCES) generate-ini-sqlite
|
||||
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\#%
|
||||
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/$*
|
||||
|
||||
e2e.mysql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.mysql.test
|
||||
|
||||
e2e.mysql8.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.mysql8.test
|
||||
|
||||
e2e.pgsql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.pgsql.test
|
||||
|
||||
e2e.mssql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.mssql.test
|
||||
|
||||
e2e.sqlite.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/tests/e2e -o e2e.sqlite.test -tags '$(TEST_TAGS)'
|
||||
migrations.individual.sqlite.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/models/migrations -o migrations.individual.sqlite.test -tags '$(TEST_TAGS)'
|
||||
|
||||
.PHONY: check
|
||||
check: test
|
||||
|
@ -729,60 +599,58 @@ build: frontend backend
|
|||
frontend: $(WEBPACK_DEST)
|
||||
|
||||
.PHONY: backend
|
||||
backend: go-check generate-backend $(EXECUTABLE)
|
||||
backend: go-check generate $(EXECUTABLE)
|
||||
|
||||
# We generate the backend before the frontend in case we in future we want to generate things in the frontend from generated files in backend
|
||||
.PHONY: generate
|
||||
generate: generate-backend
|
||||
|
||||
.PHONY: generate-backend
|
||||
generate-backend: $(TAGS_PREREQ) generate-go
|
||||
|
||||
.PHONY: generate-go
|
||||
generate-go: $(TAGS_PREREQ)
|
||||
generate: $(TAGS_PREREQ)
|
||||
@echo "Running go generate..."
|
||||
@CC= GOOS= GOARCH= $(GO) generate -tags '$(TAGS)' $(GO_PACKAGES)
|
||||
|
||||
.PHONY: security-check
|
||||
security-check:
|
||||
go run $(GOVULNCHECK_PACKAGE) -v ./...
|
||||
@CC= GOOS= GOARCH= $(GO) generate -mod=vendor -tags '$(TAGS)' $(GO_PACKAGES)
|
||||
|
||||
$(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 -mod=vendor $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||
|
||||
.PHONY: release
|
||||
release: frontend generate release-linux release-copy release-compress vendor release-sources release-check
|
||||
release: frontend generate release-windows release-linux release-darwin release-copy release-compress release-sources release-docs release-check
|
||||
|
||||
$(DIST_DIRS):
|
||||
mkdir -p $(DIST_DIRS)
|
||||
|
||||
.PHONY: release-windows
|
||||
release-windows: | $(DIST_DIRS)
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
||||
fi
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||
ifeq (,$(findstring gogit,$(TAGS)))
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo gogit $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo gogit $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
|
||||
endif
|
||||
ifeq ($(CI),true)
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
|
||||
.PHONY: release-linux
|
||||
release-linux: | $(DIST_DIRS)
|
||||
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 forgejo-$(VERSION) .
|
||||
ifeq ($(CI),true)
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
||||
fi
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
|
||||
.PHONY: release-darwin
|
||||
release-darwin: | $(DIST_DIRS)
|
||||
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) .
|
||||
ifeq ($(CI),true)
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
||||
fi
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
|
||||
.PHONY: release-copy
|
||||
release-copy: | $(DIST_DIRS)
|
||||
cd $(DIST); for file in `find . -type f -name "*"`; do cp $${file} ./release/; done;
|
||||
cd $(DIST); for file in `find /build -type f -name "*"`; do cp $${file} ./release/; done;
|
||||
|
||||
.PHONY: release-check
|
||||
release-check: | $(DIST_DIRS)
|
||||
|
@ -790,16 +658,17 @@ release-check: | $(DIST_DIRS)
|
|||
|
||||
.PHONY: release-compress
|
||||
release-compress: | $(DIST_DIRS)
|
||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && $(GO) run $(GXZ_PAGAGE) -k -9 $${file}; done;
|
||||
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u github.com/ulikunitz/xz/cmd/gxz; \
|
||||
fi
|
||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
|
||||
|
||||
.PHONY: release-sources
|
||||
release-sources: | $(DIST_DIRS)
|
||||
echo $(VERSION) > $(STORED_VERSION_FILE)
|
||||
# bsdtar needs a ^ to prevent matching subdirectories
|
||||
$(eval EXCL := --exclude=$(shell tar --help | grep -q bsdtar && echo "^")./)
|
||||
# use transform to a add a release-folder prefix; in bsdtar the transform parameter equivalent is -s
|
||||
$(eval TRANSFORM := $(shell tar --help | grep -q bsdtar && echo "-s '/^./forgejo-src-$(VERSION)/'" || echo "--transform 's|^./|forgejo-src-$(VERSION)/|'"))
|
||||
tar $(addprefix $(EXCL),$(TAR_EXCLUDES)) $(TRANSFORM) -czf $(DIST)/release/forgejo-src-$(VERSION).tar.gz .
|
||||
tar $(addprefix $(EXCL),$(TAR_EXCLUDES)) -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz .
|
||||
rm -f $(STORED_VERSION_FILE)
|
||||
|
||||
.PHONY: release-docs
|
||||
|
@ -813,27 +682,6 @@ docs:
|
|||
fi
|
||||
cd docs; make trans-copy clean build-offline;
|
||||
|
||||
.PHONY: deps
|
||||
deps: deps-frontend deps-backend
|
||||
|
||||
.PHONY: deps-frontend
|
||||
deps-frontend: node_modules
|
||||
|
||||
.PHONY: deps-backend
|
||||
deps-backend:
|
||||
$(GO) mod download
|
||||
$(GO) install $(AIR_PACKAGE)
|
||||
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE)
|
||||
$(GO) install $(ERRCHECK_PACKAGE)
|
||||
$(GO) install $(GOFUMPT_PACKAGE)
|
||||
$(GO) install $(GOLANGCI_LINT_PACKAGE)
|
||||
$(GO) install $(GXZ_PAGAGE)
|
||||
$(GO) install $(MISSPELL_PACKAGE)
|
||||
$(GO) install $(SWAGGER_PACKAGE)
|
||||
$(GO) install $(XGO_PACKAGE)
|
||||
$(GO) install $(GO_LICENSES_PACKAGE)
|
||||
$(GO) install $(GOVULNCHECK_PACKAGE)
|
||||
|
||||
node_modules: package-lock.json
|
||||
npm install --no-save
|
||||
@touch node_modules
|
||||
|
@ -851,9 +699,8 @@ fomantic:
|
|||
cd $(FOMANTIC_WORK_DIR) && npm install --no-save
|
||||
cp -f $(FOMANTIC_WORK_DIR)/theme.config.less $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/theme.config
|
||||
cp -rf $(FOMANTIC_WORK_DIR)/_site $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/
|
||||
cp -f web_src/js/vendor/dropdown.js $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/definitions/modules
|
||||
cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build
|
||||
$(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
|
||||
webpack: $(WEBPACK_DEST)
|
||||
|
@ -879,17 +726,6 @@ svg-check: svg
|
|||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: lockfile-check
|
||||
lockfile-check:
|
||||
npm install --package-lock-only
|
||||
@diff=$$(git diff package-lock.json); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "package-lock.json is inconsistent with package.json"; \
|
||||
echo "Please run 'npm install --package-lock-only' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: update-translations
|
||||
update-translations:
|
||||
mkdir -p ./translations
|
||||
|
@ -902,15 +738,15 @@ update-translations:
|
|||
|
||||
.PHONY: generate-license
|
||||
generate-license:
|
||||
$(GO) run build/generate-licenses.go
|
||||
GO111MODULE=on $(GO) run build/generate-licenses.go
|
||||
|
||||
.PHONY: generate-gitignore
|
||||
generate-gitignore:
|
||||
$(GO) run build/generate-gitignores.go
|
||||
GO111MODULE=on $(GO) run build/generate-gitignores.go
|
||||
|
||||
.PHONY: generate-images
|
||||
generate-images: | node_modules
|
||||
npm install --no-save --no-package-lock fabric@5 imagemin-zopfli@7
|
||||
npm install --no-save --no-package-lock fabric@4 imagemin-zopfli@7
|
||||
node build/generate-images.js $(TAGS)
|
||||
|
||||
.PHONY: generate-manpage
|
||||
|
@ -919,7 +755,7 @@ generate-manpage:
|
|||
@mkdir -p man/man1/ man/man5
|
||||
@./gitea docs --man > man/man1/gitea.1
|
||||
@gzip -9 man/man1/gitea.1 && echo man/man1/gitea.1.gz created
|
||||
@#TODO A small script that formats config-cheat-sheet.en-us.md nicely for use as a config man page
|
||||
@#TODO A smal script witch format config-cheat-sheet.en-us.md nicely to suit as config man page
|
||||
|
||||
.PHONY: pr\#%
|
||||
pr\#%: clean-all
|
||||
|
@ -927,18 +763,11 @@ pr\#%: clean-all
|
|||
|
||||
.PHONY: golangci-lint
|
||||
golangci-lint:
|
||||
$(GO) run $(GOLANGCI_LINT_PACKAGE) run
|
||||
|
||||
# workaround step for the lint-backend-windows CI task because 'go run' can not
|
||||
# have distinct GOOS/GOARCH for its build and run steps
|
||||
.PHONY: golangci-lint-windows
|
||||
golangci-lint-windows:
|
||||
@GOOS= GOARCH= $(GO) install $(GOLANGCI_LINT_PACKAGE)
|
||||
golangci-lint run
|
||||
|
||||
.PHONY: editorconfig-checker
|
||||
editorconfig-checker:
|
||||
$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) templates
|
||||
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
export BINARY="golangci-lint"; \
|
||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.37.0; \
|
||||
fi
|
||||
golangci-lint run --timeout 10m
|
||||
|
||||
.PHONY: docker
|
||||
docker:
|
||||
|
|
192
README.md
192
README.md
|
@ -1,46 +1,170 @@
|
|||
# Welcome to Forgejo
|
||||
<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>
|
||||
|
||||
Hi there! Tired of big platforms playing monopoly?
|
||||
Providing Git hosting for your project, friends, company or community?
|
||||
**Forgejo** (inspired by forĝejo \ˈfor.d͡ʒe.jo\ – the Esperanto word for *forge*) has you covered with its intuitive interface,
|
||||
light and easy hosting and a lot of builtin functionality.
|
||||
<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://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://godoc.org/code.gitea.io/gitea" title="Go Report Card">
|
||||
<img src="https://goreportcard.com/badge/code.gitea.io/gitea">
|
||||
</a>
|
||||
<a href="https://godoc.org/code.gitea.io/gitea" title="GoDoc">
|
||||
<img src="https://godoc.org/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://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" title="TODOs">
|
||||
<img src="https://badgen.net/https/api.tickgit.com/badgen/github.com/go-gitea/gitea">
|
||||
</a>
|
||||
<a href="https://www.bountysource.com/teams/gitea" title="Bountysource">
|
||||
<img src="https://img.shields.io/bountysource/team/gitea/activity">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
Forgejo was forked from the well-known [Gitea](https://gitea.io) project in 2022,
|
||||
because we think that the project should be owned by an independent community instead of a for-profit company.
|
||||
If you second that, then Forgejo is for you!
|
||||
Our promise: **Independent Free/Libre Software forever!**
|
||||
<p align="center">
|
||||
<a href="README_ZH.md">View the chinese version of this document</a>
|
||||
</p>
|
||||
|
||||
## Purpose
|
||||
|
||||
## What does Forgejo offer?
|
||||
The goal of this project is to make the easiest, fastest, and most
|
||||
painless way of setting up a self-hosted Git service.
|
||||
Using Go, this can be done with an independent binary distribution across
|
||||
**all platforms** which Go supports, including Linux, macOS, and Windows
|
||||
on x86, amd64, ARM and PowerPC architectures.
|
||||
Want to try it before doing anything else?
|
||||
Do it [with the online demo](https://try.gitea.io/)!
|
||||
This project has been
|
||||
[forked](https://blog.gitea.io/2016/12/welcome-to-gitea/) from
|
||||
[Gogs](https://gogs.io) since 2016.11 but changed a lot.
|
||||
|
||||
If you want to know what Forgejo is like,
|
||||
you can check out public instances<!-- could become link to list -->,
|
||||
e.g. [Codeberg.org](https://codeberg.org).
|
||||
## Building
|
||||
|
||||
If you like any of the following, Forgejo is literally meant for you:
|
||||
From the root of the source tree, run:
|
||||
|
||||
- Lightweight: Forgejo can easily be hosted on nearly **every machine**.
|
||||
Running on a Raspberry? Small cloud instance? No problem!
|
||||
- Intuitive: We aim for a user experience that is **friendly** to newcomers and **productive** for power users.
|
||||
- Project management: Besides Git hosting, Forgejo offers issues,
|
||||
pull requests, wikis, kanban boards and much more to **coordinate with your team**.
|
||||
- Publishing: Have something to share? Use **releases** to host your software for download,
|
||||
or use the **package registry** to publish it for docker, npm and many other package managers.
|
||||
- Customizable: Want to change your look? Change some settings?
|
||||
There are many **config switches** to make Forgejo work exactly like you want.
|
||||
- Powerful: Organizations & team permissions, CI integration, Code Search, LDAP, OAuth and much more.
|
||||
If you have **advanced needs**, Forgejo has you covered.
|
||||
- Privacy: From update checker to default settings: Forgejo is built to be **privacy first** for you and your crew.
|
||||
- Federation: (WIP) We are actively working to connect software forges with each other through **ActivityPub**,
|
||||
and create a collaborative network of personal instances.
|
||||
Interested? [Read more below](#Status-of-federation)
|
||||
TAGS="bindata" make build
|
||||
|
||||
## Learn more
|
||||
or if sqlite support is required:
|
||||
|
||||
We're still working on our website.
|
||||
In the meantime, you can <a href="https://floss.social/@Forgejo" rel="me">find us on the Fediverse</a> or hop into [our Matrix room](https://matrix.to/#/#Forgejo-chat:matrix.org) if you have any questions or want to get involved.
|
||||
TAGS="bindata sqlite sqlite_unlock_notify" make build
|
||||
|
||||
The `build` target is split into two sub-targets:
|
||||
|
||||
## Get involved
|
||||
- `make backend` which requires [Go 1.13](https://golang.org/dl/) or greater.
|
||||
- `make frontend` which requires [Node.js 12.17](https://nodejs.org/en/download/) or greater and Internet connectivity to download npm dependencies.
|
||||
|
||||
If you are interested in making Forgejo better, either by reporting a bug or by changing the governance, please [take a look at the contribution guide](CONTRIBUTING.md).
|
||||
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.
|
||||
|
||||
Parallelism (`make -j <num>`) is not supported.
|
||||
|
||||
More info: https://docs.gitea.io/en-us/install-from-source/
|
||||
|
||||
## Using
|
||||
|
||||
./gitea web
|
||||
|
||||
NOTE: If you're interested in using our APIs, we have experimental
|
||||
support with [documentation](https://try.gitea.io/api/swagger).
|
||||
|
||||
## Contributing
|
||||
|
||||
Expected workflow is: Fork -> Patch -> Push -> Pull Request
|
||||
|
||||
NOTES:
|
||||
|
||||
1. **YOU MUST READ THE [CONTRIBUTORS GUIDE](CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST.**
|
||||
2. If you have found a vulnerability in the project, please write privately to **security@gitea.io**. Thanks!
|
||||
|
||||
## Translating
|
||||
|
||||
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.
|
||||
|
||||
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 fo fill it as questions pop up.
|
||||
|
||||
https://docs.gitea.io/en-us/translation-guidelines/
|
||||
|
||||
[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://crowdin.com/project/gitea)
|
||||
|
||||
## Further information
|
||||
|
||||
For more information and instructions about how to install Gitea, please look at our [documentation](https://docs.gitea.io/en-us/).
|
||||
If you have questions that are not covered by the documentation, you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create a post in the [discourse forum](https://discourse.gitea.io/).
|
||||
|
||||
We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea).
|
||||
The hugo-based documentation theme is hosted at [gitea/theme](https://gitea.com/gitea/theme).
|
||||
The official Gitea CLI is developed at [gitea/tea](https://gitea.com/gitea/tea).
|
||||
|
||||
## Authors
|
||||
|
||||
* [Maintainers](https://github.com/orgs/go-gitea/people)
|
||||
* [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
|
||||
* [Translators](options/locale/TRANSLATORS)
|
||||
|
||||
## Backers
|
||||
|
||||
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/gitea#backer)]
|
||||
|
||||
<a href="https://opencollective.com/gitea#backers" target="_blank"><img src="https://opencollective.com/gitea/backers.svg?width=890"></a>
|
||||
|
||||
## Sponsors
|
||||
|
||||
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/gitea#sponsor)]
|
||||
|
||||
<a href="https://opencollective.com/gitea/sponsor/0/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/gitea/sponsor/1/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/gitea/sponsor/2/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/gitea/sponsor/3/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/gitea/sponsor/4/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/gitea/sponsor/5/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/gitea/sponsor/6/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/gitea/sponsor/7/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/gitea/sponsor/8/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/gitea/sponsor/9/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/9/avatar.svg"></a>
|
||||
|
||||
## FAQ
|
||||
|
||||
**How do you pronounce Gitea?**
|
||||
|
||||
Gitea is pronounced [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY) as in "gi-tea" with a hard g.
|
||||
|
||||
**Why is this not hosted on a Gitea instance?**
|
||||
|
||||
We're [working on it](https://github.com/go-gitea/gitea/issues/1029).
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License.
|
||||
See the [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) file
|
||||
for the full license text.
|
||||
|
||||
## Screenshots
|
||||
Looking for an overview of the interface? Check it out!
|
||||
|
||||
|![Dashboard](https://dl.gitea.io/screenshots/home_timeline.png)|![User Profile](https://dl.gitea.io/screenshots/user_profile.png)|![Global Issues](https://dl.gitea.io/screenshots/global_issues.png)|
|
||||
|:---:|:---:|:---:|
|
||||
|![Branches](https://dl.gitea.io/screenshots/branches.png)|![Web Editor](https://dl.gitea.io/screenshots/web_editor.png)|![Activity](https://dl.gitea.io/screenshots/activity.png)|
|
||||
|![New Migration](https://dl.gitea.io/screenshots/migration.png)|![Migrating](https://dl.gitea.io/screenshots/migration.gif)|![Pull Request View](https://image.ibb.co/e02dSb/6.png)
|
||||
![Pull Request Dark](https://dl.gitea.io/screenshots/pull_requests_dark.png)|![Diff Review Dark](https://dl.gitea.io/screenshots/review_dark.png)|![Diff Dark](https://dl.gitea.io/screenshots/diff_dark.png)|
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
<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://microbadger.com/images/gitea/gitea" title="Get your own image badge on microbadger.com">
|
||||
<img src="https://images.microbadger.com/badges/image/gitea/gitea.svg">
|
||||
</a>
|
||||
<a href="https://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://godoc.org/code.gitea.io/gitea" title="Go Report Card">
|
||||
<img src="https://goreportcard.com/badge/code.gitea.io/gitea">
|
||||
</a>
|
||||
<a href="https://godoc.org/code.gitea.io/gitea" title="GoDoc">
|
||||
<img src="https://godoc.org/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://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" title="TODOs">
|
||||
<img src="https://badgen.net/https/api.tickgit.com/badgen/github.com/go-gitea/gitea">
|
||||
</a>
|
||||
<a href="https://img.shields.io/bountysource/team/gitea" title="Bountysource">
|
||||
<img src="https://img.shields.io/bountysource/team/gitea/activity">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="README.md">View the english version of this document</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进行的.
|
||||
[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](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) 文件中。
|
||||
|
||||
## 截图
|
||||
|
||||
|![Dashboard](https://dl.gitea.io/screenshots/home_timeline.png)|![User Profile](https://dl.gitea.io/screenshots/user_profile.png)|![Global Issues](https://dl.gitea.io/screenshots/global_issues.png)|
|
||||
|:---:|:---:|:---:|
|
||||
|![Branches](https://dl.gitea.io/screenshots/branches.png)|![Web Editor](https://dl.gitea.io/screenshots/web_editor.png)|![Activity](https://dl.gitea.io/screenshots/activity.png)|
|
||||
|![New Migration](https://dl.gitea.io/screenshots/migration.png)|![Migrating](https://dl.gitea.io/screenshots/migration.gif)|![Pull Request View](https://image.ibb.co/e02dSb/6.png)
|
||||
![Pull Request Dark](https://dl.gitea.io/screenshots/pull_requests_dark.png)|![Diff Review Dark](https://dl.gitea.io/screenshots/review_dark.png)|![Diff Dark](https://dl.gitea.io/screenshots/diff_dark.png)|
|
|
@ -0,0 +1,10 @@
|
|||
# Reporting security issues
|
||||
|
||||
The Gitea maintainers take security seriously.
|
||||
If you discover a security issue, please bring it to their attention right away!
|
||||
|
||||
### Reporting a Vulnerability
|
||||
|
||||
Please **DO NOT** file a public issue, instead send your report privately to `security@gitea.io`.
|
||||
|
||||
Security reports are greatly appreciated and we will publicly thank you for it, although we keep your name confidential if you request it.
|
File diff suppressed because one or more lines are too long
|
@ -1,31 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" id="main_outline" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 640 640" style="enable-background:new 0 0 640 640;" xml:space="preserve">
|
||||
<g>
|
||||
<path id="teabag" style="fill:#FFFFFF" d="M395.9,484.2l-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5,21.2-17.9,33.8-11.8
|
||||
c17.2,8.3,27.1,13,27.1,13l-0.1-109.2l16.7-0.1l0.1,117.1c0,0,57.4,24.2,83.1,40.1c3.7,2.3,10.2,6.8,12.9,14.4
|
||||
c2.1,6.1,2,13.1-1,19.3l-61,126.9C423.6,484.9,408.4,490.3,395.9,484.2z"/>
|
||||
<g>
|
||||
<g>
|
||||
<path style="fill:#609926" d="M622.7,149.8c-4.1-4.1-9.6-4-9.6-4s-117.2,6.6-177.9,8c-13.3,0.3-26.5,0.6-39.6,0.7c0,39.1,0,78.2,0,117.2
|
||||
c-5.5-2.6-11.1-5.3-16.6-7.9c0-36.4-0.1-109.2-0.1-109.2c-29,0.4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5
|
||||
c-9.8-0.6-22.5-2.1-39,1.5c-8.7,1.8-33.5,7.4-53.8,26.9C-4.9,212.4,6.6,276.2,8,285.8c1.7,11.7,6.9,44.2,31.7,72.5
|
||||
c45.8,56.1,144.4,54.8,144.4,54.8s12.1,28.9,30.6,55.5c25,33.1,50.7,58.9,75.7,62c63,0,188.9-0.1,188.9-0.1s12,0.1,28.3-10.3
|
||||
c14-8.5,26.5-23.4,26.5-23.4s12.9-13.8,30.9-45.3c5.5-9.7,10.1-19.1,14.1-28c0,0,55.2-117.1,55.2-231.1
|
||||
C633.2,157.9,624.7,151.8,622.7,149.8z M125.6,353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6,321.8,60,295.4
|
||||
c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5,38.5-30c13.8-3.7,31-3.1,31-3.1s7.1,59.4,15.7,94.2c7.2,29.2,24.8,77.7,24.8,77.7
|
||||
S142.5,359.9,125.6,353.9z M425.9,461.5c0,0-6.1,14.5-19.6,15.4c-5.8,0.4-10.3-1.2-10.3-1.2s-0.3-0.1-5.3-2.1l-112.9-55
|
||||
c0,0-10.9-5.7-12.8-15.6c-2.2-8.1,2.7-18.1,2.7-18.1L322,273c0,0,4.8-9.7,12.2-13c0.6-0.3,2.3-1,4.5-1.5c8.1-2.1,18,2.8,18,2.8
|
||||
l110.7,53.7c0,0,12.6,5.7,15.3,16.2c1.9,7.4-0.5,14-1.8,17.2C474.6,363.8,425.9,461.5,425.9,461.5z"/>
|
||||
<path style="fill:#609926" d="M326.8,380.1c-8.2,0.1-15.4,5.8-17.3,13.8c-1.9,8,2,16.3,9.1,20c7.7,4,17.5,1.8,22.7-5.4
|
||||
c5.1-7.1,4.3-16.9-1.8-23.1l24-49.1c1.5,0.1,3.7,0.2,6.2-0.5c4.1-0.9,7.1-3.6,7.1-3.6c4.2,1.8,8.6,3.8,13.2,6.1
|
||||
c4.8,2.4,9.3,4.9,13.4,7.3c0.9,0.5,1.8,1.1,2.8,1.9c1.6,1.3,3.4,3.1,4.7,5.5c1.9,5.5-1.9,14.9-1.9,14.9
|
||||
c-2.3,7.6-18.4,40.6-18.4,40.6c-8.1-0.2-15.3,5-17.7,12.5c-2.6,8.1,1.1,17.3,8.9,21.3c7.8,4,17.4,1.7,22.5-5.3
|
||||
c5-6.8,4.6-16.3-1.1-22.6c1.9-3.7,3.7-7.4,5.6-11.3c5-10.4,13.5-30.4,13.5-30.4c0.9-1.7,5.7-10.3,2.7-21.3
|
||||
c-2.5-11.4-12.6-16.7-12.6-16.7c-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3c4.7-9.7,9.4-19.3,14.1-29
|
||||
c-4.1-2-8.1-4-12.2-6.1c-4.8,9.8-9.7,19.7-14.5,29.5c-6.7-0.1-12.9,3.5-16.1,9.4c-3.4,6.3-2.7,14.1,1.9,19.8
|
||||
C343.2,346.5,335,363.3,326.8,380.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.5 KiB |
File diff suppressed because one or more lines are too long
5
build.go
5
build.go
|
@ -1,8 +1,9 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build vendor
|
||||
// +build vendor
|
||||
|
||||
package main
|
||||
|
||||
|
|
|
@ -1,291 +0,0 @@
|
|||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/build/codeformat"
|
||||
)
|
||||
|
||||
// Windows has a limitation for command line arguments, the size can not exceed 32KB.
|
||||
// So we have to feed the files to some tools (like gofmt) batch by batch
|
||||
|
||||
// We also introduce a `gitea-fmt` command, it does better import formatting than gofmt/goimports. `gitea-fmt` calls `gofmt` internally.
|
||||
|
||||
var optionLogVerbose bool
|
||||
|
||||
func logVerbose(msg string, args ...interface{}) {
|
||||
if optionLogVerbose {
|
||||
log.Printf(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func passThroughCmd(cmd string, args []string) error {
|
||||
foundCmd, err := exec.LookPath(cmd)
|
||||
if err != nil {
|
||||
log.Fatalf("can not find cmd: %s", cmd)
|
||||
}
|
||||
c := exec.Cmd{
|
||||
Path: foundCmd,
|
||||
Args: append([]string{cmd}, args...),
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
}
|
||||
return c.Run()
|
||||
}
|
||||
|
||||
type fileCollector struct {
|
||||
dirs []string
|
||||
includePatterns []*regexp.Regexp
|
||||
excludePatterns []*regexp.Regexp
|
||||
batchSize int
|
||||
}
|
||||
|
||||
func newFileCollector(fileFilter string, batchSize int) (*fileCollector, error) {
|
||||
co := &fileCollector{batchSize: batchSize}
|
||||
if fileFilter == "go-own" {
|
||||
co.dirs = []string{
|
||||
"build",
|
||||
"cmd",
|
||||
"contrib",
|
||||
"tests",
|
||||
"models",
|
||||
"modules",
|
||||
"routers",
|
||||
"services",
|
||||
"tools",
|
||||
}
|
||||
co.includePatterns = append(co.includePatterns, regexp.MustCompile(`.*\.go$`))
|
||||
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`.*\bbindata\.go$`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/gitea-repositories-meta`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/integration/migration-test`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`modules/git/tests`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`models/fixtures`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`models/migrations/fixtures`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`services/gitdiff/testdata`))
|
||||
}
|
||||
|
||||
if co.dirs == nil {
|
||||
return nil, fmt.Errorf("unknown file-filter: %s", fileFilter)
|
||||
}
|
||||
return co, nil
|
||||
}
|
||||
|
||||
func (fc *fileCollector) matchPatterns(path string, regexps []*regexp.Regexp) bool {
|
||||
path = strings.ReplaceAll(path, "\\", "/")
|
||||
for _, re := range regexps {
|
||||
if re.MatchString(path) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (fc *fileCollector) collectFiles() (res [][]string, err error) {
|
||||
var batch []string
|
||||
for _, dir := range fc.dirs {
|
||||
err = filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
|
||||
include := len(fc.includePatterns) == 0 || fc.matchPatterns(path, fc.includePatterns)
|
||||
exclude := fc.matchPatterns(path, fc.excludePatterns)
|
||||
process := include && !exclude
|
||||
if !process {
|
||||
if d.IsDir() {
|
||||
if exclude {
|
||||
logVerbose("exclude dir %s", path)
|
||||
return filepath.SkipDir
|
||||
}
|
||||
// for a directory, if it is not excluded explicitly, we should walk into
|
||||
return nil
|
||||
}
|
||||
// for a file, we skip it if it shouldn't be processed
|
||||
logVerbose("skip process %s", path)
|
||||
return nil
|
||||
}
|
||||
if d.IsDir() {
|
||||
// skip dir, we don't add dirs to the file list now
|
||||
return nil
|
||||
}
|
||||
if len(batch) >= fc.batchSize {
|
||||
res = append(res, batch)
|
||||
batch = nil
|
||||
}
|
||||
batch = append(batch, path)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
res = append(res, batch)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// substArgFiles expands the {file-list} to a real file list for commands
|
||||
func substArgFiles(args, files []string) []string {
|
||||
for i, s := range args {
|
||||
if s == "{file-list}" {
|
||||
newArgs := append(args[:i], files...)
|
||||
newArgs = append(newArgs, args[i+1:]...)
|
||||
return newArgs
|
||||
}
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func exitWithCmdErrors(subCmd string, subArgs []string, cmdErrors []error) {
|
||||
for _, err := range cmdErrors {
|
||||
if err != nil {
|
||||
if exitError, ok := err.(*exec.ExitError); ok {
|
||||
exitCode := exitError.ExitCode()
|
||||
log.Printf("run command failed (code=%d): %s %v", exitCode, subCmd, subArgs)
|
||||
os.Exit(exitCode)
|
||||
} else {
|
||||
log.Fatalf("run command failed (err=%s) %s %v", err, subCmd, subArgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseArgs() (mainOptions map[string]string, subCmd string, subArgs []string) {
|
||||
mainOptions = map[string]string{}
|
||||
for i := 1; i < len(os.Args); i++ {
|
||||
arg := os.Args[i]
|
||||
if arg == "" {
|
||||
break
|
||||
}
|
||||
if arg[0] == '-' {
|
||||
arg = strings.TrimPrefix(arg, "-")
|
||||
arg = strings.TrimPrefix(arg, "-")
|
||||
fields := strings.SplitN(arg, "=", 2)
|
||||
if len(fields) == 1 {
|
||||
mainOptions[fields[0]] = "1"
|
||||
} else {
|
||||
mainOptions[fields[0]] = fields[1]
|
||||
}
|
||||
} else {
|
||||
subCmd = arg
|
||||
subArgs = os.Args[i+1:]
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func showUsage() {
|
||||
fmt.Printf(`Usage: %[1]s [options] {command} [arguments]
|
||||
|
||||
Options:
|
||||
--verbose
|
||||
--file-filter=go-own
|
||||
--batch-size=100
|
||||
|
||||
Commands:
|
||||
%[1]s gofmt ...
|
||||
|
||||
Arguments:
|
||||
{file-list} the file list
|
||||
|
||||
Example:
|
||||
%[1]s gofmt -s -d {file-list}
|
||||
|
||||
`, "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) {
|
||||
fileFilter := mainOptions["file-filter"]
|
||||
if fileFilter == "" {
|
||||
fileFilter = "go-own"
|
||||
}
|
||||
batchSize, _ := strconv.Atoi(mainOptions["batch-size"])
|
||||
if batchSize == 0 {
|
||||
batchSize = 100
|
||||
}
|
||||
|
||||
return newFileCollector(fileFilter, batchSize)
|
||||
}
|
||||
|
||||
func containsString(a []string, s string) bool {
|
||||
for _, v := range a {
|
||||
if v == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func giteaFormatGoImports(files []string, doWriteFile bool) error {
|
||||
for _, file := range files {
|
||||
if err := codeformat.FormatGoImports(file, doWriteFile); err != nil {
|
||||
log.Printf("failed to format go imports: %s, err=%v", file, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
mainOptions, subCmd, subArgs := parseArgs()
|
||||
if subCmd == "" {
|
||||
showUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
optionLogVerbose = mainOptions["verbose"] != ""
|
||||
|
||||
fc, err := newFileCollectorFromMainOptions(mainOptions)
|
||||
if err != nil {
|
||||
log.Fatalf("can not create file collector: %s", err.Error())
|
||||
}
|
||||
|
||||
fileBatches, err := fc.collectFiles()
|
||||
if err != nil {
|
||||
log.Fatalf("can not collect files: %s", err.Error())
|
||||
}
|
||||
|
||||
processed := 0
|
||||
var cmdErrors []error
|
||||
for _, files := range fileBatches {
|
||||
if len(files) == 0 {
|
||||
break
|
||||
}
|
||||
substArgs := substArgFiles(subArgs, files)
|
||||
logVerbose("batch cmd: %s %v", subCmd, substArgs)
|
||||
switch subCmd {
|
||||
case "gitea-fmt":
|
||||
if containsString(subArgs, "-d") {
|
||||
log.Print("the -d option is not supported by gitea-fmt")
|
||||
}
|
||||
cmdErrors = append(cmdErrors, giteaFormatGoImports(files, containsString(subArgs, "-w")))
|
||||
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra", "-lang", getGoVersion()}, substArgs...)))
|
||||
default:
|
||||
log.Fatalf("unknown cmd: %s %v", subCmd, subArgs)
|
||||
}
|
||||
processed += len(files)
|
||||
}
|
||||
|
||||
logVerbose("processed %d files", processed)
|
||||
exitWithCmdErrors(subCmd, subArgs, cmdErrors)
|
||||
}
|
|
@ -1,195 +0,0 @@
|
|||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codeformat
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var importPackageGroupOrders = map[string]int{
|
||||
"": 1, // internal
|
||||
"code.gitea.io/gitea/": 2,
|
||||
}
|
||||
|
||||
var errInvalidCommentBetweenImports = errors.New("comments between imported packages are invalid, please move comments to the end of the package line")
|
||||
|
||||
var (
|
||||
importBlockBegin = []byte("\nimport (\n")
|
||||
importBlockEnd = []byte("\n)")
|
||||
)
|
||||
|
||||
type importLineParsed struct {
|
||||
group string
|
||||
pkg string
|
||||
content string
|
||||
}
|
||||
|
||||
func parseImportLine(line string) (*importLineParsed, error) {
|
||||
il := &importLineParsed{content: line}
|
||||
p1 := strings.IndexRune(line, '"')
|
||||
if p1 == -1 {
|
||||
return nil, errors.New("invalid import line: " + line)
|
||||
}
|
||||
p1++
|
||||
p := strings.IndexRune(line[p1:], '"')
|
||||
if p == -1 {
|
||||
return nil, errors.New("invalid import line: " + line)
|
||||
}
|
||||
p2 := p1 + p
|
||||
il.pkg = line[p1:p2]
|
||||
|
||||
pDot := strings.IndexRune(il.pkg, '.')
|
||||
pSlash := strings.IndexRune(il.pkg, '/')
|
||||
if pDot != -1 && pDot < pSlash {
|
||||
il.group = "domain-package"
|
||||
}
|
||||
for groupName := range importPackageGroupOrders {
|
||||
if groupName == "" {
|
||||
continue // skip internal
|
||||
}
|
||||
if strings.HasPrefix(il.pkg, groupName) {
|
||||
il.group = groupName
|
||||
}
|
||||
}
|
||||
return il, nil
|
||||
}
|
||||
|
||||
type (
|
||||
importLineGroup []*importLineParsed
|
||||
importLineGroupMap map[string]importLineGroup
|
||||
)
|
||||
|
||||
func formatGoImports(contentBytes []byte) ([]byte, error) {
|
||||
p1 := bytes.Index(contentBytes, importBlockBegin)
|
||||
if p1 == -1 {
|
||||
return nil, nil
|
||||
}
|
||||
p1 += len(importBlockBegin)
|
||||
p := bytes.Index(contentBytes[p1:], importBlockEnd)
|
||||
if p == -1 {
|
||||
return nil, nil
|
||||
}
|
||||
p2 := p1 + p
|
||||
|
||||
importGroups := importLineGroupMap{}
|
||||
r := bytes.NewBuffer(contentBytes[p1:p2])
|
||||
eof := false
|
||||
for !eof {
|
||||
line, err := r.ReadString('\n')
|
||||
eof = err == io.EOF
|
||||
if err != nil && !eof {
|
||||
return nil, err
|
||||
}
|
||||
line = strings.TrimSpace(line)
|
||||
if line != "" {
|
||||
if strings.HasPrefix(line, "//") || strings.HasPrefix(line, "/*") {
|
||||
return nil, errInvalidCommentBetweenImports
|
||||
}
|
||||
importLine, err := parseImportLine(line)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
importGroups[importLine.group] = append(importGroups[importLine.group], importLine)
|
||||
}
|
||||
}
|
||||
|
||||
var groupNames []string
|
||||
for groupName, importLines := range importGroups {
|
||||
groupNames = append(groupNames, groupName)
|
||||
sort.Slice(importLines, func(i, j int) bool {
|
||||
return strings.Compare(importLines[i].pkg, importLines[j].pkg) < 0
|
||||
})
|
||||
}
|
||||
|
||||
sort.Slice(groupNames, func(i, j int) bool {
|
||||
n1 := groupNames[i]
|
||||
n2 := groupNames[j]
|
||||
o1 := importPackageGroupOrders[n1]
|
||||
o2 := importPackageGroupOrders[n2]
|
||||
if o1 != 0 && o2 != 0 {
|
||||
return o1 < o2
|
||||
}
|
||||
if o1 == 0 && o2 == 0 {
|
||||
return strings.Compare(n1, n2) < 0
|
||||
}
|
||||
return o1 != 0
|
||||
})
|
||||
|
||||
formattedBlock := bytes.Buffer{}
|
||||
for _, groupName := range groupNames {
|
||||
hasNormalImports := false
|
||||
hasDummyImports := false
|
||||
// non-dummy import comes first
|
||||
for _, importLine := range importGroups[groupName] {
|
||||
if strings.HasPrefix(importLine.content, "_") {
|
||||
hasDummyImports = true
|
||||
} else {
|
||||
formattedBlock.WriteString("\t" + importLine.content + "\n")
|
||||
hasNormalImports = true
|
||||
}
|
||||
}
|
||||
// dummy (_ "pkg") comes later
|
||||
if hasDummyImports {
|
||||
if hasNormalImports {
|
||||
formattedBlock.WriteString("\n")
|
||||
}
|
||||
for _, importLine := range importGroups[groupName] {
|
||||
if strings.HasPrefix(importLine.content, "_") {
|
||||
formattedBlock.WriteString("\t" + importLine.content + "\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
formattedBlock.WriteString("\n")
|
||||
}
|
||||
formattedBlockBytes := bytes.TrimRight(formattedBlock.Bytes(), "\n")
|
||||
|
||||
var formattedBytes []byte
|
||||
formattedBytes = append(formattedBytes, contentBytes[:p1]...)
|
||||
formattedBytes = append(formattedBytes, formattedBlockBytes...)
|
||||
formattedBytes = append(formattedBytes, contentBytes[p2:]...)
|
||||
return formattedBytes, nil
|
||||
}
|
||||
|
||||
// FormatGoImports format the imports by our rules (see unit tests)
|
||||
func FormatGoImports(file string, doWriteFile bool) error {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var contentBytes []byte
|
||||
{
|
||||
defer f.Close()
|
||||
contentBytes, err = io.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
formattedBytes, err := formatGoImports(contentBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if formattedBytes == nil {
|
||||
return nil
|
||||
}
|
||||
if bytes.Equal(contentBytes, formattedBytes) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if doWriteFile {
|
||||
f, err = os.OpenFile(file, os.O_TRUNC|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = f.Write(formattedBytes)
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package codeformat
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFormatImportsSimple(t *testing.T) {
|
||||
formatted, err := formatGoImports([]byte(`
|
||||
package codeformat
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
`))
|
||||
|
||||
expected := `
|
||||
package codeformat
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
`
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, string(formatted))
|
||||
}
|
||||
|
||||
func TestFormatImportsGroup(t *testing.T) {
|
||||
// gofmt/goimports won't group the packages, for example, they produce such code:
|
||||
// "bytes"
|
||||
// "image"
|
||||
// (a blank line)
|
||||
// "fmt"
|
||||
// "image/color/palette"
|
||||
// our formatter does better, and these packages are grouped into one.
|
||||
|
||||
formatted, err := formatGoImports([]byte(`
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
_ "image/gif" // for processing gif images
|
||||
_ "image/jpeg" // for processing jpeg images
|
||||
_ "image/png" // for processing png images
|
||||
|
||||
"code.gitea.io/other/package"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/the/package"
|
||||
|
||||
"github.com/issue9/identicon"
|
||||
"github.com/nfnt/resize"
|
||||
"github.com/oliamb/cutter"
|
||||
)
|
||||
`))
|
||||
|
||||
expected := `
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
_ "image/gif" // for processing gif images
|
||||
_ "image/jpeg" // for processing jpeg images
|
||||
_ "image/png" // for processing png images
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"code.gitea.io/other/package"
|
||||
"github.com/issue9/identicon"
|
||||
"github.com/nfnt/resize"
|
||||
"github.com/oliamb/cutter"
|
||||
"xorm.io/the/package"
|
||||
)
|
||||
`
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, string(formatted))
|
||||
}
|
||||
|
||||
func TestFormatImportsInvalidComment(t *testing.T) {
|
||||
// why we shouldn't write comments between imports: it breaks the grouping of imports
|
||||
// for example:
|
||||
// "pkg1"
|
||||
// "pkg2"
|
||||
// // a comment
|
||||
// "pkgA"
|
||||
// "pkgB"
|
||||
// the comment splits the packages into two groups, pkg1/2 are sorted separately, pkgA/B are sorted separately
|
||||
// we don't want such code, so the code should be:
|
||||
// "pkg1"
|
||||
// "pkg2"
|
||||
// "pkgA" // a comment
|
||||
// "pkgB"
|
||||
|
||||
_, err := formatGoImports([]byte(`
|
||||
package test
|
||||
|
||||
import (
|
||||
"image/jpeg"
|
||||
// for processing gif images
|
||||
"image/gif"
|
||||
)
|
||||
`))
|
||||
assert.ErrorIs(t, err, errInvalidCommentBetweenImports)
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
|
@ -9,6 +11,7 @@ import (
|
|||
"bytes"
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
@ -18,14 +21,14 @@ import (
|
|||
"github.com/shurcooL/vfsgen"
|
||||
)
|
||||
|
||||
func needsUpdate(dir, filename string) (bool, []byte) {
|
||||
func needsUpdate(dir string, filename string) (bool, []byte) {
|
||||
needRegen := false
|
||||
_, err := os.Stat(filename)
|
||||
if err != nil {
|
||||
needRegen = true
|
||||
}
|
||||
|
||||
oldHash, err := os.ReadFile(filename + ".hash")
|
||||
oldHash, err := ioutil.ReadFile(filename + ".hash")
|
||||
if err != nil {
|
||||
oldHash = []byte{}
|
||||
}
|
||||
|
@ -48,6 +51,7 @@ func needsUpdate(dir, filename string) (bool, []byte) {
|
|||
newHash := hasher.Sum([]byte{})
|
||||
|
||||
if bytes.Compare(oldHash, newHash) != 0 {
|
||||
|
||||
return true, newHash
|
||||
}
|
||||
|
||||
|
@ -55,15 +59,11 @@ func needsUpdate(dir, filename string) (bool, []byte) {
|
|||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 4 {
|
||||
if len(os.Args) != 4 {
|
||||
log.Fatal("Insufficient number of arguments. Need: directory packageName filename")
|
||||
}
|
||||
|
||||
dir, packageName, filename := os.Args[1], os.Args[2], os.Args[3]
|
||||
var useGlobalModTime bool
|
||||
if len(os.Args) == 5 {
|
||||
useGlobalModTime, _ = strconv.ParseBool(os.Args[4])
|
||||
}
|
||||
|
||||
update, newHash := needsUpdate(dir, filename)
|
||||
|
||||
|
@ -75,14 +75,13 @@ func main() {
|
|||
fmt.Printf("generating bindata for %s\n", packageName)
|
||||
var fsTemplates http.FileSystem = http.Dir(dir)
|
||||
err := vfsgen.Generate(fsTemplates, vfsgen.Options{
|
||||
PackageName: packageName,
|
||||
BuildTags: "bindata",
|
||||
VariableName: "Assets",
|
||||
Filename: filename,
|
||||
UseGlobalModTime: useGlobalModTime,
|
||||
PackageName: packageName,
|
||||
BuildTags: "bindata",
|
||||
VariableName: "Assets",
|
||||
Filename: filename,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("%v\n", err)
|
||||
}
|
||||
_ = os.WriteFile(filename+".hash", newHash, 0o666)
|
||||
_ = ioutil.WriteFile(filename+".hash", newHash, 0666)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2015 Kenneth Shaw
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
|
@ -10,17 +12,16 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -28,7 +29,9 @@ const (
|
|||
maxUnicodeVersion = 12
|
||||
)
|
||||
|
||||
var flagOut = flag.String("o", "modules/emoji/emoji_data.go", "out")
|
||||
var (
|
||||
flagOut = flag.String("o", "modules/emoji/emoji_data.go", "out")
|
||||
)
|
||||
|
||||
// Gemoji is a set of emoji data.
|
||||
type Gemoji []Emoji
|
||||
|
@ -49,6 +52,7 @@ func (e Emoji) MarshalJSON() ([]byte, error) {
|
|||
x.UnicodeVersion = ""
|
||||
x.Description = ""
|
||||
x.SkinTones = false
|
||||
json := jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
return json.Marshal(x)
|
||||
}
|
||||
|
||||
|
@ -64,7 +68,7 @@ func main() {
|
|||
}
|
||||
|
||||
// write
|
||||
err = os.WriteFile(*flagOut, buf, 0o644)
|
||||
err = ioutil.WriteFile(*flagOut, buf, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -93,19 +97,20 @@ func generate() ([]byte, error) {
|
|||
defer res.Body.Close()
|
||||
|
||||
// read all
|
||||
body, err := io.ReadAll(res.Body)
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// unmarshal
|
||||
var data Gemoji
|
||||
json := jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
skinTones := make(map[string]string)
|
||||
var skinTones = make(map[string]string)
|
||||
|
||||
skinTones["\U0001f3fb"] = "Light Skin Tone"
|
||||
skinTones["\U0001f3fc"] = "Medium-Light Skin Tone"
|
||||
|
@ -115,7 +120,7 @@ func generate() ([]byte, error) {
|
|||
|
||||
var tmp Gemoji
|
||||
|
||||
// filter out emoji that require greater than max unicode version
|
||||
//filter out emoji that require greater than max unicode version
|
||||
for i := range data {
|
||||
val, _ := strconv.ParseFloat(data[i].UnicodeVersion, 64)
|
||||
if int(val) <= maxUnicodeVersion {
|
||||
|
@ -154,7 +159,7 @@ func generate() ([]byte, error) {
|
|||
|
||||
// write a JSON file to use with tribute (write before adding skin tones since we can't support them there yet)
|
||||
file, _ := json.Marshal(data)
|
||||
_ = os.WriteFile("assets/emoji.json", file, 0o644)
|
||||
_ = ioutil.WriteFile("assets/emoji.json", file, 0644)
|
||||
|
||||
// Add skin tones to emoji that support it
|
||||
var (
|
||||
|
@ -208,12 +213,13 @@ func generate() ([]byte, error) {
|
|||
|
||||
const hdr = `
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package emoji
|
||||
|
||||
// Code generated by build/generate-emoji.go. DO NOT EDIT.
|
||||
// Code generated by gen.go. DO NOT EDIT.
|
||||
// Sourced from %s
|
||||
//
|
||||
var GemojiData = %#v
|
||||
`
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
|
@ -8,6 +9,7 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
@ -32,7 +34,8 @@ func main() {
|
|||
flag.StringVar(&githubApiToken, "token", "", "github api token")
|
||||
flag.Parse()
|
||||
|
||||
file, err := os.CreateTemp(os.TempDir(), prefix)
|
||||
file, err := ioutil.TempFile(os.TempDir(), prefix)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create temp file. %s", err)
|
||||
}
|
||||
|
@ -63,6 +66,7 @@ func main() {
|
|||
}
|
||||
|
||||
gz, err := gzip.NewReader(file)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to gunzip the archive. %s", err)
|
||||
}
|
||||
|
@ -93,6 +97,7 @@ func main() {
|
|||
}
|
||||
|
||||
out, err := os.Create(path.Join(destination, strings.TrimSuffix(filepath.Base(hdr.Name), ".gitignore")))
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create new file. %s", err)
|
||||
}
|
||||
|
@ -109,13 +114,13 @@ func main() {
|
|||
for dst, src := range filesToCopy {
|
||||
// Read all content of src to data
|
||||
src = path.Join(destination, src)
|
||||
data, err := os.ReadFile(src)
|
||||
data, err := ioutil.ReadFile(src)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read src file. %s", err)
|
||||
}
|
||||
// Write data to dst
|
||||
dst = path.Join(destination, dst)
|
||||
err = os.WriteFile(dst, data, 0o644)
|
||||
err = ioutil.WriteFile(dst, data, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to write new file. %s", err)
|
||||
}
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// regexp is based on go-license, excluding README and NOTICE
|
||||
// https://github.com/google/go-licenses/blob/master/licenses/find.go
|
||||
var licenseRe = regexp.MustCompile(`^(?i)((UN)?LICEN(S|C)E|COPYING).*$`)
|
||||
|
||||
type LicenseEntry struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
LicenseText string `json:"licenseText"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
base, out := os.Args[1], os.Args[2]
|
||||
|
||||
paths := []string{}
|
||||
err := filepath.WalkDir(base, func(path string, entry fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if entry.IsDir() || !licenseRe.MatchString(entry.Name()) {
|
||||
return nil
|
||||
}
|
||||
paths = append(paths, path)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sort.Strings(paths)
|
||||
|
||||
entries := []LicenseEntry{}
|
||||
for _, path := range paths {
|
||||
licenseText, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
path := strings.Replace(path, base+string(os.PathSeparator), "", 1)
|
||||
name := filepath.Dir(path)
|
||||
|
||||
// There might be a bug somewhere in go-licenses that sometimes interprets the
|
||||
// root package as "." and sometimes as "code.gitea.io/gitea". Workaround by
|
||||
// removing both of them for the sake of stable output.
|
||||
if name == "." || name == "code.gitea.io/gitea" {
|
||||
continue
|
||||
}
|
||||
|
||||
entries = append(entries, LicenseEntry{
|
||||
Name: name,
|
||||
Path: path,
|
||||
LicenseText: string(licenseText),
|
||||
})
|
||||
}
|
||||
|
||||
jsonBytes, err := json.MarshalIndent(entries, "", " ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(out, jsonBytes, 0o644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
|
@ -1,8 +1,13 @@
|
|||
#!/usr/bin/env node
|
||||
import imageminZopfli from 'imagemin-zopfli';
|
||||
import {optimize} from 'svgo';
|
||||
import {optimize, extendDefaultPlugins} from 'svgo';
|
||||
import {fabric} from 'fabric';
|
||||
import {readFile, writeFile} from 'fs/promises';
|
||||
import fs from 'fs';
|
||||
import {resolve, dirname} from 'path';
|
||||
import {fileURLToPath} from 'url';
|
||||
|
||||
const {readFile, writeFile} = fs.promises;
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const logoFile = resolve(__dirname, '../assets/logo.svg');
|
||||
|
||||
function exit(err) {
|
||||
if (err) console.error(err);
|
||||
|
@ -17,19 +22,16 @@ function loadSvg(svg) {
|
|||
});
|
||||
}
|
||||
|
||||
async function generate(svg, path, {size, bg}) {
|
||||
const outputFile = new URL(path, import.meta.url);
|
||||
|
||||
if (String(outputFile).endsWith('.svg')) {
|
||||
async function generate(svg, outputFile, {size, bg}) {
|
||||
if (outputFile.endsWith('.svg')) {
|
||||
const {data} = optimize(svg, {
|
||||
plugins: [
|
||||
'preset-default',
|
||||
plugins: extendDefaultPlugins([
|
||||
'removeDimensions',
|
||||
{
|
||||
name: 'addAttributesToSVGElement',
|
||||
params: {attributes: [{width: size}, {height: size}]}
|
||||
},
|
||||
],
|
||||
]),
|
||||
});
|
||||
await writeFile(outputFile, data);
|
||||
return;
|
||||
|
@ -65,18 +67,17 @@ async function generate(svg, path, {size, bg}) {
|
|||
|
||||
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');
|
||||
const svg = await readFile(logoFile, '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}),
|
||||
generate(svg, resolve(__dirname, '../public/img/logo.svg'), {size: 32}),
|
||||
generate(svg, resolve(__dirname, '../public/img/logo.png'), {size: 512}),
|
||||
generate(svg, resolve(__dirname, '../public/img/favicon.png'), {size: 180}),
|
||||
generate(svg, resolve(__dirname, '../public/img/avatar_default.png'), {size: 200}),
|
||||
generate(svg, resolve(__dirname, '../public/img/apple-touch-icon.png'), {size: 180, bg: true}),
|
||||
gitea && generate(svg, resolve(__dirname, '../public/img/gitea.svg'), {size: 32}),
|
||||
]);
|
||||
}
|
||||
|
||||
main().then(exit).catch(exit);
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
|
@ -8,6 +9,7 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
@ -32,21 +34,14 @@ func main() {
|
|||
flag.StringVar(&githubApiToken, "token", "", "github api token")
|
||||
flag.Parse()
|
||||
|
||||
file, err := os.CreateTemp(os.TempDir(), prefix)
|
||||
file, err := ioutil.TempFile(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)
|
||||
|
@ -72,6 +67,7 @@ func main() {
|
|||
}
|
||||
|
||||
gz, err := gzip.NewReader(file)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to gunzip the archive. %s", err)
|
||||
}
|
||||
|
@ -105,6 +101,7 @@ func main() {
|
|||
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)
|
||||
}
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
#!/usr/bin/env node
|
||||
import fastGlob from 'fast-glob';
|
||||
import {optimize} from 'svgo';
|
||||
import {parse} from 'path';
|
||||
import {readFile, writeFile, mkdir} from 'fs/promises';
|
||||
import {optimize, extendDefaultPlugins} from 'svgo';
|
||||
import {resolve, parse, dirname} from 'path';
|
||||
import fs from 'fs';
|
||||
import {fileURLToPath} from 'url';
|
||||
|
||||
const glob = (pattern) => fastGlob.sync(pattern, {
|
||||
cwd: fileURLToPath(new URL('..', import.meta.url)),
|
||||
absolute: true,
|
||||
});
|
||||
const {readFile, writeFile, mkdir} = fs.promises;
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const glob = (pattern) => fastGlob.sync(pattern, {cwd: resolve(__dirname), absolute: true});
|
||||
const outputDir = resolve(__dirname, '../public/img/svg');
|
||||
|
||||
function exit(err) {
|
||||
if (err) console.error(err);
|
||||
|
@ -17,6 +16,7 @@ function exit(err) {
|
|||
|
||||
async function processFile(file, {prefix, fullName} = {}) {
|
||||
let name;
|
||||
|
||||
if (fullName) {
|
||||
name = fullName;
|
||||
} else {
|
||||
|
@ -26,17 +26,21 @@ async function processFile(file, {prefix, fullName} = {}) {
|
|||
}
|
||||
|
||||
const {data} = optimize(await readFile(file, 'utf8'), {
|
||||
plugins: [
|
||||
{name: 'preset-default'},
|
||||
{name: 'removeXMLNS'},
|
||||
{name: 'removeDimensions'},
|
||||
plugins: extendDefaultPlugins([
|
||||
'removeXMLNS',
|
||||
'removeDimensions',
|
||||
{name: 'prefixIds', params: {prefix: () => name}},
|
||||
{name: 'addClassesToSVGElement', params: {classNames: ['svg', name]}},
|
||||
{name: 'addAttributesToSVGElement', params: {attributes: [{'width': '16'}, {'height': '16'}, {'aria-hidden': 'true'}]}},
|
||||
],
|
||||
{
|
||||
name: 'addClassesToSVGElement',
|
||||
params: {classNames: ['svg', name]},
|
||||
},
|
||||
{
|
||||
name: 'addAttributesToSVGElement',
|
||||
params: {attributes: [{'width': '16'}, {'height': '16'}, {'aria-hidden': 'true'}]},
|
||||
},
|
||||
]),
|
||||
});
|
||||
|
||||
await writeFile(fileURLToPath(new URL(`../public/img/svg/${name}.svg`, import.meta.url)), data);
|
||||
await writeFile(resolve(outputDir, `${name}.svg`), data);
|
||||
}
|
||||
|
||||
function processFiles(pattern, opts) {
|
||||
|
@ -45,14 +49,15 @@ function processFiles(pattern, opts) {
|
|||
|
||||
async function main() {
|
||||
try {
|
||||
await mkdir(fileURLToPath(new URL('../public/img/svg', import.meta.url)), {recursive: true});
|
||||
await mkdir(outputDir);
|
||||
} 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'}),
|
||||
...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);
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Copyright (c) 2015, Wade Simmons
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// gocovmerge takes the results from multiple `go test -coverprofile` runs and
|
||||
// merges them into one profile
|
||||
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
|
@ -20,7 +22,7 @@ import (
|
|||
"golang.org/x/tools/cover"
|
||||
)
|
||||
|
||||
func mergeProfiles(p, merge *cover.Profile) {
|
||||
func mergeProfiles(p *cover.Profile, merge *cover.Profile) {
|
||||
if p.Mode != merge.Mode {
|
||||
log.Fatalf("cannot merge profiles with different modes")
|
||||
}
|
||||
|
@ -107,7 +109,7 @@ func main() {
|
|||
for _, file := range flag.Args() {
|
||||
profiles, err := cover.ParseProfiles(file)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to parse profile '%s': %v", file, err)
|
||||
log.Fatalf("failed to parse profiles: %v", err)
|
||||
}
|
||||
for _, p := range profiles {
|
||||
merged = addProfile(merged, p)
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if [ ! -f ./build/test-env-check.sh ]; then
|
||||
echo "${0} can only be executed in gitea source root directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
echo "check uid ..."
|
||||
|
||||
# the uid of gitea defined in "https://gitea.com/gitea/test-env" is 1000
|
||||
gitea_uid=$(id -u gitea)
|
||||
if [ "$gitea_uid" != "1000" ]; then
|
||||
echo "The uid of linux user 'gitea' is expected to be 1000, but it is $gitea_uid"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cur_uid=$(id -u)
|
||||
if [ "$cur_uid" != "0" -a "$cur_uid" != "$gitea_uid" ]; then
|
||||
echo "The uid of current linux user is expected to be 0 or $gitea_uid, but it is $cur_uid"
|
||||
exit 1
|
||||
fi
|
|
@ -1,11 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if [ ! -f ./build/test-env-prepare.sh ]; then
|
||||
echo "${0} can only be executed in gitea source root directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "change the owner of files to gitea ..."
|
||||
chown -R gitea:gitea .
|
479
cmd/admin.go
479
cmd/admin.go
|
@ -1,6 +1,7 @@
|
|||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
|
@ -12,11 +13,8 @@ import (
|
|||
"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"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/auth/oauth2"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
@ -24,12 +22,6 @@ import (
|
|||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
"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"
|
||||
user_service "code.gitea.io/gitea/services/user"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
@ -56,7 +48,6 @@ var (
|
|||
microcmdUserList,
|
||||
microcmdUserChangePassword,
|
||||
microcmdUserDelete,
|
||||
microcmdUserGenerateAccessToken,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -114,10 +105,6 @@ var (
|
|||
Name: "access-token",
|
||||
Usage: "Generate access token for the user",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "restricted",
|
||||
Usage: "Make a restricted user account",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -155,35 +142,10 @@ var (
|
|||
Name: "email,e",
|
||||
Usage: "Email of the user to delete",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "purge",
|
||||
Usage: "Purge user, all their repositories, organizations and comments",
|
||||
},
|
||||
},
|
||||
Action: runDeleteUser,
|
||||
}
|
||||
|
||||
microcmdUserGenerateAccessToken = cli.Command{
|
||||
Name: "generate-access-token",
|
||||
Usage: "Generate a access token for a specific user",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "username,u",
|
||||
Usage: "Username",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "token-name,t",
|
||||
Usage: "Token name",
|
||||
Value: "gitea-admin",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "raw",
|
||||
Usage: "Display only the token value",
|
||||
},
|
||||
},
|
||||
Action: runGenerateAccessToken,
|
||||
}
|
||||
|
||||
subcmdRepoSyncReleases = cli.Command{
|
||||
Name: "repo-sync-releases",
|
||||
Usage: "Synchronize repository releases with tags",
|
||||
|
@ -221,8 +183,6 @@ var (
|
|||
cmdAuthUpdateLdapBindDn,
|
||||
cmdAuthAddLdapSimpleAuth,
|
||||
cmdAuthUpdateLdapSimpleAuth,
|
||||
microcmdAuthAddSMTP,
|
||||
microcmdAuthUpdateSMTP,
|
||||
microcmdAuthList,
|
||||
microcmdAuthDelete,
|
||||
},
|
||||
|
@ -328,40 +288,6 @@ var (
|
|||
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",
|
||||
},
|
||||
}
|
||||
|
||||
microcmdAuthUpdateOauth = cli.Command{
|
||||
|
@ -399,72 +325,6 @@ 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.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 runChangePassword(c *cli.Context) error {
|
||||
|
@ -472,10 +332,7 @@ func runChangePassword(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(c.String("password")) < setting.MinPasswordLength {
|
||||
|
@ -493,7 +350,7 @@ func runChangePassword(c *cli.Context) error {
|
|||
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")
|
||||
}
|
||||
uname := c.String("username")
|
||||
user, err := user_model.GetUserByName(ctx, uname)
|
||||
user, err := models.GetUserByName(uname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -501,7 +358,7 @@ func runChangePassword(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err = user_model.UpdateUserCols(ctx, user, "passwd", "passwd_hash_algo", "salt"); err != nil {
|
||||
if err = models.UpdateUserCols(user, "passwd", "passwd_hash_algo", "salt"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -533,10 +390,7 @@ func runCreateUser(c *cli.Context) error {
|
|||
fmt.Fprintf(os.Stderr, "--name flag is deprecated. Use --username instead.\n")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -555,11 +409,11 @@ func runCreateUser(c *cli.Context) error {
|
|||
}
|
||||
|
||||
// always default to true
|
||||
changePassword := true
|
||||
var changePassword = true
|
||||
|
||||
// If this is the first user being created.
|
||||
// Take it as the admin and don't force a password update.
|
||||
if n := user_model.CountUsers(nil); n == 0 {
|
||||
if n := models.CountUsers(); n == 0 {
|
||||
changePassword = false
|
||||
}
|
||||
|
||||
|
@ -567,36 +421,27 @@ func runCreateUser(c *cli.Context) error {
|
|||
changePassword = c.Bool("must-change-password")
|
||||
}
|
||||
|
||||
restricted := util.OptionalBoolNone
|
||||
|
||||
if c.IsSet("restricted") {
|
||||
restricted = util.OptionalBoolOf(c.Bool("restricted"))
|
||||
}
|
||||
|
||||
u := &user_model.User{
|
||||
u := &models.User{
|
||||
Name: username,
|
||||
Email: c.String("email"),
|
||||
Passwd: password,
|
||||
IsActive: true,
|
||||
IsAdmin: c.Bool("admin"),
|
||||
MustChangePassword: changePassword,
|
||||
Theme: setting.UI.DefaultTheme,
|
||||
}
|
||||
|
||||
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
||||
IsActive: util.OptionalBoolTrue,
|
||||
IsRestricted: restricted,
|
||||
}
|
||||
|
||||
if err := user_model.CreateUser(u, overwriteDefault); err != nil {
|
||||
return fmt.Errorf("CreateUser: %w", err)
|
||||
if err := models.CreateUser(u); err != nil {
|
||||
return fmt.Errorf("CreateUser: %v", err)
|
||||
}
|
||||
|
||||
if c.Bool("access-token") {
|
||||
t := &auth_model.AccessToken{
|
||||
t := &models.AccessToken{
|
||||
Name: "gitea-admin",
|
||||
UID: u.ID,
|
||||
}
|
||||
|
||||
if err := auth_model.NewAccessToken(t); err != nil {
|
||||
if err := models.NewAccessToken(t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -608,14 +453,12 @@ func runCreateUser(c *cli.Context) error {
|
|||
}
|
||||
|
||||
func runListUsers(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
users, err := user_model.GetAllUsers()
|
||||
users, err := models.GetAllUsers()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -630,16 +473,16 @@ func runListUsers(c *cli.Context) error {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
twofa := user_model.UserList(users).GetTwoFaStatus()
|
||||
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n")
|
||||
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\n")
|
||||
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\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
w.Flush()
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func runDeleteUser(c *cli.Context) error {
|
||||
|
@ -647,10 +490,7 @@ func runDeleteUser(c *cli.Context) error {
|
|||
return fmt.Errorf("You must provide the id, username or email of a user to delete")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -659,13 +499,13 @@ func runDeleteUser(c *cli.Context) error {
|
|||
}
|
||||
|
||||
var err error
|
||||
var user *user_model.User
|
||||
var user *models.User
|
||||
if c.IsSet("email") {
|
||||
user, err = user_model.GetUserByEmail(c.String("email"))
|
||||
user, err = models.GetUserByEmail(c.String("email"))
|
||||
} else if c.IsSet("username") {
|
||||
user, err = user_model.GetUserByName(ctx, c.String("username"))
|
||||
user, err = models.GetUserByName(c.String("username"))
|
||||
} else {
|
||||
user, err = user_model.GetUserByID(ctx, c.Int64("id"))
|
||||
user, err = models.GetUserByID(c.Int64("id"))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -678,63 +518,25 @@ func runDeleteUser(c *cli.Context) error {
|
|||
return fmt.Errorf("The user %s does not match the provided id %d", user.Name, c.Int64("id"))
|
||||
}
|
||||
|
||||
return user_service.DeleteUser(ctx, user, c.Bool("purge"))
|
||||
}
|
||||
|
||||
func runGenerateAccessToken(c *cli.Context) error {
|
||||
if !c.IsSet("username") {
|
||||
return fmt.Errorf("You must provide the username to generate a token for them")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user, err := user_model.GetUserByName(ctx, c.String("username"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t := &auth_model.AccessToken{
|
||||
Name: c.String("token-name"),
|
||||
UID: user.ID,
|
||||
}
|
||||
|
||||
if err := auth_model.NewAccessToken(t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Bool("raw") {
|
||||
fmt.Printf("%s\n", t.Token)
|
||||
} else {
|
||||
fmt.Printf("Access token was successfully created: %s\n", t.Token)
|
||||
}
|
||||
|
||||
return nil
|
||||
return models.DeleteUser(user)
|
||||
}
|
||||
|
||||
func runRepoSyncReleases(_ *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("Synchronizing repository releases (this may take a while)")
|
||||
for page := 1; ; page++ {
|
||||
repos, count, err := repo_model.SearchRepositoryByName(ctx, &repo_model.SearchRepoOptions{
|
||||
ListOptions: db.ListOptions{
|
||||
PageSize: repo_model.RepositoryListDefaultPageSize,
|
||||
repos, count, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
|
||||
ListOptions: models.ListOptions{
|
||||
PageSize: models.RepositoryListDefaultPageSize,
|
||||
Page: page,
|
||||
},
|
||||
Private: true,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("SearchRepositoryByName: %w", err)
|
||||
return fmt.Errorf("SearchRepositoryByName: %v", err)
|
||||
}
|
||||
if len(repos) == 0 {
|
||||
break
|
||||
|
@ -742,7 +544,7 @@ func runRepoSyncReleases(_ *cli.Context) error {
|
|||
log.Trace("Processing next %d repos of %d", len(repos), count)
|
||||
for _, repo := range repos {
|
||||
log.Trace("Synchronizing repo %s with path %s", repo.FullName(), repo.RepoPath())
|
||||
gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())
|
||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||
if err != nil {
|
||||
log.Warn("OpenRepository: %v", err)
|
||||
continue
|
||||
|
@ -777,35 +579,29 @@ func runRepoSyncReleases(_ *cli.Context) error {
|
|||
}
|
||||
|
||||
func getReleaseCount(id int64) (int64, error) {
|
||||
return repo_model.GetReleaseCountByRepoID(
|
||||
return models.GetReleaseCountByRepoID(
|
||||
id,
|
||||
repo_model.FindReleasesOptions{
|
||||
models.FindReleasesOptions{
|
||||
IncludeTags: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func runRegenerateHooks(_ *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
|
||||
return repo_module.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
|
||||
}
|
||||
|
||||
func runRegenerateKeys(_ *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
return asymkey_model.RewriteAllPublicKeys()
|
||||
return models.RewriteAllPublicKeys()
|
||||
}
|
||||
|
||||
func parseOAuth2Config(c *cli.Context) *oauth2.Source {
|
||||
func parseOAuth2Config(c *cli.Context) *models.OAuth2Config {
|
||||
var customURLMapping *oauth2.CustomURLMapping
|
||||
if c.IsSet("use-custom-urls") {
|
||||
customURLMapping = &oauth2.CustomURLMapping{
|
||||
|
@ -817,36 +613,26 @@ func parseOAuth2Config(c *cli.Context) *oauth2.Source {
|
|||
} else {
|
||||
customURLMapping = nil
|
||||
}
|
||||
return &oauth2.Source{
|
||||
return &models.OAuth2Config{
|
||||
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"),
|
||||
}
|
||||
}
|
||||
|
||||
func runAddOauth(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return auth_model.CreateSource(&auth_model.Source{
|
||||
Type: auth_model.OAuth2,
|
||||
Name: c.String("name"),
|
||||
IsActive: true,
|
||||
Cfg: parseOAuth2Config(c),
|
||||
return models.CreateLoginSource(&models.LoginSource{
|
||||
Type: models.LoginOAuth2,
|
||||
Name: c.String("name"),
|
||||
IsActived: true,
|
||||
Cfg: parseOAuth2Config(c),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -855,19 +641,16 @@ func runUpdateOauth(c *cli.Context) error {
|
|||
return fmt.Errorf("--id flag is missing")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
source, err := auth_model.GetSourceByID(c.Int64("id"))
|
||||
source, err := models.GetLoginSourceByID(c.Int64("id"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oAuth2Config := source.Cfg.(*oauth2.Source)
|
||||
oAuth2Config := source.OAuth2()
|
||||
|
||||
if c.IsSet("name") {
|
||||
source.Name = c.String("name")
|
||||
|
@ -893,29 +676,8 @@ func runUpdateOauth(c *cli.Context) error {
|
|||
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")
|
||||
}
|
||||
|
||||
// update custom URL mapping
|
||||
customURLMapping := &oauth2.CustomURLMapping{}
|
||||
var customURLMapping = &oauth2.CustomURLMapping{}
|
||||
|
||||
if oAuth2Config.CustomURLMapping != nil {
|
||||
customURLMapping.TokenURL = oAuth2Config.CustomURLMapping.TokenURL
|
||||
|
@ -942,130 +704,16 @@ func runUpdateOauth(c *cli.Context) error {
|
|||
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 !contains(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)
|
||||
return models.UpdateSource(source)
|
||||
}
|
||||
|
||||
func runListAuth(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authSources, err := auth_model.Sources()
|
||||
loginSources, err := models.LoginSources()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1083,8 +731,8 @@ func runListAuth(c *cli.Context) error {
|
|||
// 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)
|
||||
for _, source := range loginSources {
|
||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, models.LoginNames[source.Type], source.IsActived)
|
||||
}
|
||||
w.Flush()
|
||||
|
||||
|
@ -1096,17 +744,14 @@ func runDeleteAuth(c *cli.Context) error {
|
|||
return fmt.Errorf("--id flag is missing")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
source, err := auth_model.GetSourceByID(c.Int64("id"))
|
||||
source, err := models.GetLoginSourceByID(c.Int64("id"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return auth_service.DeleteSource(source)
|
||||
return models.DeleteSource(source)
|
||||
}
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/services/auth/source/ldap"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/auth/ldap"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
type (
|
||||
authService struct {
|
||||
initDB func(ctx context.Context) error
|
||||
createAuthSource func(*auth.Source) error
|
||||
updateAuthSource func(*auth.Source) error
|
||||
getAuthSourceByID func(id int64) (*auth.Source, error)
|
||||
initDB func() error
|
||||
createLoginSource func(loginSource *models.LoginSource) error
|
||||
updateLoginSource func(loginSource *models.LoginSource) error
|
||||
getLoginSourceByID func(id int64) (*models.LoginSource, error)
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -33,10 +33,6 @@ var (
|
|||
Name: "not-active",
|
||||
Usage: "Deactivate the authentication source.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "active",
|
||||
Usage: "Activate the authentication source.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "security-protocol",
|
||||
Usage: "Security protocol name.",
|
||||
|
@ -93,14 +89,6 @@ var (
|
|||
Name: "public-ssh-key-attribute",
|
||||
Usage: "The attribute of the user’s LDAP record containing the user’s public ssh key.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "skip-local-2fa",
|
||||
Usage: "Set to true to skip local 2fa for users authenticated by this source",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "avatar-attribute",
|
||||
Usage: "The attribute of the user’s LDAP record containing the user’s avatar.",
|
||||
},
|
||||
}
|
||||
|
||||
ldapBindDnCLIFlags = append(commonLdapCLIFlags,
|
||||
|
@ -120,10 +108,6 @@ var (
|
|||
Name: "synchronize-users",
|
||||
Usage: "Enable user synchronization.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "disable-synchronize-users",
|
||||
Usage: "Disable user synchronization.",
|
||||
},
|
||||
cli.UintFlag{
|
||||
Name: "page-size",
|
||||
Usage: "Search page size.",
|
||||
|
@ -175,103 +159,91 @@ var (
|
|||
// newAuthService creates a service with default functions.
|
||||
func newAuthService() *authService {
|
||||
return &authService{
|
||||
initDB: initDB,
|
||||
createAuthSource: auth.CreateSource,
|
||||
updateAuthSource: auth.UpdateSource,
|
||||
getAuthSourceByID: auth.GetSourceByID,
|
||||
initDB: initDB,
|
||||
createLoginSource: models.CreateLoginSource,
|
||||
updateLoginSource: models.UpdateSource,
|
||||
getLoginSourceByID: models.GetLoginSourceByID,
|
||||
}
|
||||
}
|
||||
|
||||
// parseAuthSource assigns values on authSource according to command line flags.
|
||||
func parseAuthSource(c *cli.Context, authSource *auth.Source) {
|
||||
// parseLoginSource assigns values on loginSource according to command line flags.
|
||||
func parseLoginSource(c *cli.Context, loginSource *models.LoginSource) {
|
||||
if c.IsSet("name") {
|
||||
authSource.Name = c.String("name")
|
||||
loginSource.Name = c.String("name")
|
||||
}
|
||||
if c.IsSet("not-active") {
|
||||
authSource.IsActive = !c.Bool("not-active")
|
||||
}
|
||||
if c.IsSet("active") {
|
||||
authSource.IsActive = c.Bool("active")
|
||||
loginSource.IsActived = !c.Bool("not-active")
|
||||
}
|
||||
if c.IsSet("synchronize-users") {
|
||||
authSource.IsSyncEnabled = c.Bool("synchronize-users")
|
||||
}
|
||||
if c.IsSet("disable-synchronize-users") {
|
||||
authSource.IsSyncEnabled = !c.Bool("disable-synchronize-users")
|
||||
loginSource.IsSyncEnabled = c.Bool("synchronize-users")
|
||||
}
|
||||
}
|
||||
|
||||
// parseLdapConfig assigns values on config according to command line flags.
|
||||
func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
|
||||
func parseLdapConfig(c *cli.Context, config *models.LDAPConfig) error {
|
||||
if c.IsSet("name") {
|
||||
config.Name = c.String("name")
|
||||
config.Source.Name = c.String("name")
|
||||
}
|
||||
if c.IsSet("host") {
|
||||
config.Host = c.String("host")
|
||||
config.Source.Host = c.String("host")
|
||||
}
|
||||
if c.IsSet("port") {
|
||||
config.Port = c.Int("port")
|
||||
config.Source.Port = c.Int("port")
|
||||
}
|
||||
if c.IsSet("security-protocol") {
|
||||
p, ok := findLdapSecurityProtocolByName(c.String("security-protocol"))
|
||||
if !ok {
|
||||
return fmt.Errorf("Unknown security protocol name: %s", c.String("security-protocol"))
|
||||
}
|
||||
config.SecurityProtocol = p
|
||||
config.Source.SecurityProtocol = p
|
||||
}
|
||||
if c.IsSet("skip-tls-verify") {
|
||||
config.SkipVerify = c.Bool("skip-tls-verify")
|
||||
config.Source.SkipVerify = c.Bool("skip-tls-verify")
|
||||
}
|
||||
if c.IsSet("bind-dn") {
|
||||
config.BindDN = c.String("bind-dn")
|
||||
config.Source.BindDN = c.String("bind-dn")
|
||||
}
|
||||
if c.IsSet("user-dn") {
|
||||
config.UserDN = c.String("user-dn")
|
||||
config.Source.UserDN = c.String("user-dn")
|
||||
}
|
||||
if c.IsSet("bind-password") {
|
||||
config.BindPassword = c.String("bind-password")
|
||||
config.Source.BindPassword = c.String("bind-password")
|
||||
}
|
||||
if c.IsSet("user-search-base") {
|
||||
config.UserBase = c.String("user-search-base")
|
||||
config.Source.UserBase = c.String("user-search-base")
|
||||
}
|
||||
if c.IsSet("username-attribute") {
|
||||
config.AttributeUsername = c.String("username-attribute")
|
||||
config.Source.AttributeUsername = c.String("username-attribute")
|
||||
}
|
||||
if c.IsSet("firstname-attribute") {
|
||||
config.AttributeName = c.String("firstname-attribute")
|
||||
config.Source.AttributeName = c.String("firstname-attribute")
|
||||
}
|
||||
if c.IsSet("surname-attribute") {
|
||||
config.AttributeSurname = c.String("surname-attribute")
|
||||
config.Source.AttributeSurname = c.String("surname-attribute")
|
||||
}
|
||||
if c.IsSet("email-attribute") {
|
||||
config.AttributeMail = c.String("email-attribute")
|
||||
config.Source.AttributeMail = c.String("email-attribute")
|
||||
}
|
||||
if c.IsSet("attributes-in-bind") {
|
||||
config.AttributesInBind = c.Bool("attributes-in-bind")
|
||||
config.Source.AttributesInBind = c.Bool("attributes-in-bind")
|
||||
}
|
||||
if c.IsSet("public-ssh-key-attribute") {
|
||||
config.AttributeSSHPublicKey = c.String("public-ssh-key-attribute")
|
||||
}
|
||||
if c.IsSet("avatar-attribute") {
|
||||
config.AttributeAvatar = c.String("avatar-attribute")
|
||||
config.Source.AttributeSSHPublicKey = c.String("public-ssh-key-attribute")
|
||||
}
|
||||
if c.IsSet("page-size") {
|
||||
config.SearchPageSize = uint32(c.Uint("page-size"))
|
||||
config.Source.SearchPageSize = uint32(c.Uint("page-size"))
|
||||
}
|
||||
if c.IsSet("user-filter") {
|
||||
config.Filter = c.String("user-filter")
|
||||
config.Source.Filter = c.String("user-filter")
|
||||
}
|
||||
if c.IsSet("admin-filter") {
|
||||
config.AdminFilter = c.String("admin-filter")
|
||||
config.Source.AdminFilter = c.String("admin-filter")
|
||||
}
|
||||
if c.IsSet("restricted-filter") {
|
||||
config.RestrictedFilter = c.String("restricted-filter")
|
||||
config.Source.RestrictedFilter = c.String("restricted-filter")
|
||||
}
|
||||
if c.IsSet("allow-deactivate-all") {
|
||||
config.AllowDeactivateAll = c.Bool("allow-deactivate-all")
|
||||
}
|
||||
if c.IsSet("skip-local-2fa") {
|
||||
config.SkipLocalTwoFA = c.Bool("skip-local-2fa")
|
||||
config.Source.AllowDeactivateAll = c.Bool("allow-deactivate-all")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -279,7 +251,7 @@ func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
|
|||
// findLdapSecurityProtocolByName finds security protocol by its name ignoring case.
|
||||
// It returns the value of the security protocol and if it was found.
|
||||
func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
|
||||
for i, n := range ldap.SecurityProtocolNames {
|
||||
for i, n := range models.SecurityProtocolNames {
|
||||
if strings.EqualFold(name, n) {
|
||||
return i, true
|
||||
}
|
||||
|
@ -287,23 +259,23 @@ func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
|
|||
return 0, false
|
||||
}
|
||||
|
||||
// getAuthSource gets the login source by its id defined in the command line flags.
|
||||
// getLoginSource gets the login source by its id defined in the command line flags.
|
||||
// It returns an error if the id is not set, does not match any source or if the source is not of expected type.
|
||||
func (a *authService) getAuthSource(c *cli.Context, authType auth.Type) (*auth.Source, error) {
|
||||
func (a *authService) getLoginSource(c *cli.Context, loginType models.LoginType) (*models.LoginSource, error) {
|
||||
if err := argsSet(c, "id"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authSource, err := a.getAuthSourceByID(c.Int64("id"))
|
||||
loginSource, err := a.getLoginSourceByID(c.Int64("id"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if authSource.Type != authType {
|
||||
return nil, fmt.Errorf("Invalid authentication type. expected: %s, actual: %s", authType.String(), authSource.Type.String())
|
||||
if loginSource.Type != loginType {
|
||||
return nil, fmt.Errorf("Invalid authentication type. expected: %s, actual: %s", models.LoginNames[loginType], models.LoginNames[loginSource.Type])
|
||||
}
|
||||
|
||||
return authSource, nil
|
||||
return loginSource, nil
|
||||
}
|
||||
|
||||
// addLdapBindDn adds a new LDAP via Bind DN authentication source.
|
||||
|
@ -312,49 +284,45 @@ func (a *authService) addLdapBindDn(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := a.initDB(ctx); err != nil {
|
||||
if err := a.initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authSource := &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
IsActive: true, // active by default
|
||||
Cfg: &ldap.Source{
|
||||
Enabled: true, // always true
|
||||
loginSource := &models.LoginSource{
|
||||
Type: models.LoginLDAP,
|
||||
IsActived: true, // active by default
|
||||
Cfg: &models.LDAPConfig{
|
||||
Source: &ldap.Source{
|
||||
Enabled: true, // always true
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
parseAuthSource(c, authSource)
|
||||
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
||||
parseLoginSource(c, loginSource)
|
||||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.createAuthSource(authSource)
|
||||
return a.createLoginSource(loginSource)
|
||||
}
|
||||
|
||||
// updateLdapBindDn updates a new LDAP via Bind DN authentication source.
|
||||
func (a *authService) updateLdapBindDn(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := a.initDB(ctx); err != nil {
|
||||
if err := a.initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authSource, err := a.getAuthSource(c, auth.LDAP)
|
||||
loginSource, err := a.getLoginSource(c, models.LoginLDAP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parseAuthSource(c, authSource)
|
||||
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
||||
parseLoginSource(c, loginSource)
|
||||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.updateAuthSource(authSource)
|
||||
return a.updateLoginSource(loginSource)
|
||||
}
|
||||
|
||||
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
|
||||
|
@ -363,47 +331,43 @@ func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := a.initDB(ctx); err != nil {
|
||||
if err := a.initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authSource := &auth.Source{
|
||||
Type: auth.DLDAP,
|
||||
IsActive: true, // active by default
|
||||
Cfg: &ldap.Source{
|
||||
Enabled: true, // always true
|
||||
loginSource := &models.LoginSource{
|
||||
Type: models.LoginDLDAP,
|
||||
IsActived: true, // active by default
|
||||
Cfg: &models.LDAPConfig{
|
||||
Source: &ldap.Source{
|
||||
Enabled: true, // always true
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
parseAuthSource(c, authSource)
|
||||
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
||||
parseLoginSource(c, loginSource)
|
||||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.createAuthSource(authSource)
|
||||
return a.createLoginSource(loginSource)
|
||||
}
|
||||
|
||||
// updateLdapBindDn updates a new LDAP (simple auth) authentication source.
|
||||
func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := a.initDB(ctx); err != nil {
|
||||
if err := a.initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authSource, err := a.getAuthSource(c, auth.DLDAP)
|
||||
loginSource, err := a.getLoginSource(c, models.LoginDLDAP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parseAuthSource(c, authSource)
|
||||
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
||||
parseLoginSource(c, loginSource)
|
||||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.updateAuthSource(authSource)
|
||||
return a.updateLoginSource(loginSource)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,8 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
|
@ -179,7 +180,7 @@ func runCert(c *cli.Context) error {
|
|||
}
|
||||
log.Println("Written cert.pem")
|
||||
|
||||
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
|
||||
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open key.pem for writing: %v", err)
|
||||
}
|
||||
|
|
29
cmd/cmd.go
29
cmd/cmd.go
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package cmd provides subcommands to the gitea binary - such as "web" or
|
||||
// "admin".
|
||||
|
@ -14,8 +15,7 @@ import (
|
|||
"strings"
|
||||
"syscall"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
|
@ -30,7 +30,7 @@ func argsSet(c *cli.Context, args ...string) error {
|
|||
return errors.New(a + " is not set")
|
||||
}
|
||||
|
||||
if util.IsEmptyString(c.String(a)) {
|
||||
if util.IsEmptyString(a) {
|
||||
return errors.New(a + " is required")
|
||||
}
|
||||
}
|
||||
|
@ -56,18 +56,17 @@ func confirm() (bool, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func initDB(ctx context.Context) error {
|
||||
setting.LoadFromExisting()
|
||||
setting.InitDBConfig()
|
||||
setting.NewXORMLogService(false)
|
||||
func initDB() error {
|
||||
return initDBDisableConsole(false)
|
||||
}
|
||||
|
||||
if setting.Database.Type == "" {
|
||||
log.Fatal(`Database settings are missing from the configuration file: %q.
|
||||
Ensure you are running in the correct environment or set the correct configuration file with -c.
|
||||
If this is the intended configuration file complete the [database] section.`, setting.CustomConf)
|
||||
}
|
||||
if err := db.InitEngine(ctx); err != nil {
|
||||
return fmt.Errorf("unable to initialize the database using the configuration in %q. Error: %w", setting.CustomConf, err)
|
||||
func initDBDisableConsole(disableConsole bool) error {
|
||||
setting.NewContext()
|
||||
setting.InitDBConfig()
|
||||
|
||||
setting.NewXORMLogService(disableConsole)
|
||||
if err := models.SetEngine(); err != nil {
|
||||
return fmt.Errorf("models.SetEngine: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
|
@ -22,10 +23,7 @@ var CmdConvert = cli.Command{
|
|||
}
|
||||
|
||||
func runConvert(ctx *cli.Context) error {
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(stdCtx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -33,14 +31,14 @@ func runConvert(ctx *cli.Context) error {
|
|||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Info("Custom path: %s", setting.CustomPath)
|
||||
log.Info("Log path: %s", setting.LogRootPath)
|
||||
log.Info("Configuration file: %s", setting.CustomConf)
|
||||
setting.InitDBConfig()
|
||||
|
||||
if !setting.Database.UseMySQL {
|
||||
fmt.Println("This command can only be used with a MySQL database")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := db.ConvertUtf8ToUtf8mb4(); err != nil {
|
||||
if err := models.ConvertUtf8ToUtf8mb4(); err != nil {
|
||||
log.Fatal("Failed to convert database from utf8 to utf8mb4: %v", err)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
|
|
|
@ -1,32 +1,33 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"context"
|
||||
"fmt"
|
||||
golog "log"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/migrations"
|
||||
migrate_base "code.gitea.io/gitea/models/migrations/base"
|
||||
"code.gitea.io/gitea/modules/doctor"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"xorm.io/xorm"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// CmdDoctor represents the available doctor sub-command.
|
||||
var CmdDoctor = cli.Command{
|
||||
Name: "doctor",
|
||||
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.",
|
||||
Usage: "Diagnose problems",
|
||||
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration.",
|
||||
Action: runDoctor,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
|
@ -87,7 +88,7 @@ func runRecreateTable(ctx *cli.Context) error {
|
|||
golog.SetPrefix("")
|
||||
golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT)))
|
||||
|
||||
setting.LoadFromExisting()
|
||||
setting.NewContext()
|
||||
setting.InitDBConfig()
|
||||
|
||||
setting.EnableXORMLog = ctx.Bool("debug")
|
||||
|
@ -95,10 +96,7 @@ func runRecreateTable(ctx *cli.Context) error {
|
|||
setting.Cfg.Section("log").Key("XORM").SetValue(",")
|
||||
|
||||
setting.NewXORMLogService(!ctx.Bool("debug"))
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := db.InitEngine(stdCtx); err != nil {
|
||||
if err := models.SetEngine(); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.")
|
||||
return nil
|
||||
|
@ -110,25 +108,32 @@ func runRecreateTable(ctx *cli.Context) error {
|
|||
names = append(names, args.Get(i))
|
||||
}
|
||||
|
||||
beans, err := db.NamesToBean(names...)
|
||||
beans, err := models.NamesToBean(names...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
recreateTables := migrate_base.RecreateTables(beans...)
|
||||
recreateTables := migrations.RecreateTables(beans...)
|
||||
|
||||
return db.InitEngineWithMigration(stdCtx, func(x *xorm.Engine) error {
|
||||
return models.NewEngine(context.Background(), func(x *xorm.Engine) error {
|
||||
if err := migrations.EnsureUpToDate(x); err != nil {
|
||||
return err
|
||||
}
|
||||
return recreateTables(x)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func setDoctorLogger(ctx *cli.Context) {
|
||||
func runDoctor(ctx *cli.Context) error {
|
||||
// Silence the default loggers
|
||||
log.DelNamedLogger("console")
|
||||
log.DelNamedLogger(log.DEFAULT)
|
||||
|
||||
// Now setup our own
|
||||
logFile := ctx.String("log-file")
|
||||
if !ctx.IsSet("log-file") {
|
||||
logFile = "doctor.log"
|
||||
}
|
||||
|
||||
colorize := log.CanColorStdout
|
||||
if ctx.IsSet("color") {
|
||||
colorize = ctx.Bool("color")
|
||||
|
@ -136,50 +141,11 @@ func setDoctorLogger(ctx *cli.Context) {
|
|||
|
||||
if len(logFile) == 0 {
|
||||
log.NewLogger(1000, "doctor", "console", fmt.Sprintf(`{"level":"NONE","stacktracelevel":"NONE","colorize":%t}`, colorize))
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
recovered := recover()
|
||||
if recovered == nil {
|
||||
return
|
||||
}
|
||||
|
||||
err, ok := recovered.(error)
|
||||
if !ok {
|
||||
panic(recovered)
|
||||
}
|
||||
if errors.Is(err, os.ErrPermission) {
|
||||
fmt.Fprintf(os.Stderr, "ERROR: Unable to write logs to provided file due to permissions error: %s\n %v\n", logFile, err)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "ERROR: Unable to write logs to provided file: %s\n %v\n", logFile, err)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "WARN: Logging will be disabled\n Use `--log-file` to configure log file location\n")
|
||||
log.NewLogger(1000, "doctor", "console", fmt.Sprintf(`{"level":"NONE","stacktracelevel":"NONE","colorize":%t}`, colorize))
|
||||
}()
|
||||
|
||||
if logFile == "-" {
|
||||
} else if logFile == "-" {
|
||||
log.NewLogger(1000, "doctor", "console", fmt.Sprintf(`{"level":"trace","stacktracelevel":"NONE","colorize":%t}`, colorize))
|
||||
} else {
|
||||
log.NewLogger(1000, "doctor", "file", fmt.Sprintf(`{"filename":%q,"level":"trace","stacktracelevel":"NONE"}`, logFile))
|
||||
}
|
||||
}
|
||||
|
||||
func runDoctor(ctx *cli.Context) error {
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
// Silence the default loggers
|
||||
log.DelNamedLogger("console")
|
||||
log.DelNamedLogger(log.DEFAULT)
|
||||
|
||||
// Now setup our own
|
||||
setDoctorLogger(ctx)
|
||||
|
||||
colorize := log.CanColorStdout
|
||||
if ctx.IsSet("color") {
|
||||
colorize = ctx.Bool("color")
|
||||
}
|
||||
|
||||
// Finally redirect the default golog to here
|
||||
golog.SetFlags(0)
|
||||
|
@ -234,7 +200,7 @@ func runDoctor(ctx *cli.Context) error {
|
|||
|
||||
// Now we can set up our own logger to return information about what the doctor is doing
|
||||
if err := log.NewNamedLogger("doctorouter",
|
||||
0,
|
||||
1000,
|
||||
"console",
|
||||
"console",
|
||||
fmt.Sprintf(`{"level":"INFO","stacktracelevel":"NONE","colorize":%t,"flags":-1}`, colorize)); err != nil {
|
||||
|
@ -244,5 +210,5 @@ func runDoctor(ctx *cli.Context) error {
|
|||
|
||||
logger := log.GetLogger("doctorouter")
|
||||
defer logger.Close()
|
||||
return doctor.RunChecks(stdCtx, logger, ctx.Bool("fix"), checks)
|
||||
return doctor.RunChecks(logger, ctx.Bool("fix"), checks)
|
||||
}
|
||||
|
|
122
cmd/dump.go
122
cmd/dump.go
|
@ -1,45 +1,35 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"gitea.com/go-chi/session"
|
||||
"github.com/mholt/archiver/v3"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
archiver "github.com/mholt/archiver/v3"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func addReader(w archiver.Writer, r io.ReadCloser, info os.FileInfo, customName string, verbose bool) error {
|
||||
func addFile(w archiver.Writer, filePath string, absPath string, verbose bool) error {
|
||||
if verbose {
|
||||
log.Info("Adding file %s", customName)
|
||||
log.Info("Adding file %s\n", filePath)
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -50,10 +40,16 @@ func addFile(w archiver.Writer, filePath, absPath string, verbose bool) error {
|
|||
return err
|
||||
}
|
||||
|
||||
return addReader(w, file, fileInfo, filePath, verbose)
|
||||
return w.Write(archiver.File{
|
||||
FileInfo: archiver.FileInfo{
|
||||
FileInfo: fileInfo,
|
||||
CustomName: filePath,
|
||||
},
|
||||
ReadCloser: file,
|
||||
})
|
||||
}
|
||||
|
||||
func isSubdir(upper, lower string) (bool, error) {
|
||||
func isSubdir(upper string, lower string) (bool, error) {
|
||||
if relPath, err := filepath.Rel(upper, lower); err != nil {
|
||||
return false, err
|
||||
} else if relPath == "." || !strings.HasPrefix(relPath, ".") {
|
||||
|
@ -91,7 +87,7 @@ func (o outputType) String() string {
|
|||
}
|
||||
|
||||
var outputTypeEnum = &outputType{
|
||||
Enum: []string{"zip", "tar", "tar.sz", "tar.gz", "tar.xz", "tar.bz2", "tar.br", "tar.lz4", "tar.zst"},
|
||||
Enum: []string{"zip", "rar", "tar", "sz", "tar.gz", "tar.xz", "tar.bz2", "tar.br", "tar.lz4"},
|
||||
Default: "zip",
|
||||
}
|
||||
|
||||
|
@ -141,14 +137,6 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
|
|||
Name: "skip-attachment-data",
|
||||
Usage: "Skip attachment data",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "skip-package-data",
|
||||
Usage: "Skip package data",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "skip-index",
|
||||
Usage: "Skip bleve index data",
|
||||
},
|
||||
cli.GenericFlag{
|
||||
Name: "type",
|
||||
Value: outputTypeEnum,
|
||||
|
@ -173,16 +161,10 @@ func runDump(ctx *cli.Context) error {
|
|||
fatal("Deleting default logger failed. Can not write to stdout: %v", err)
|
||||
}
|
||||
} else {
|
||||
for _, suffix := range outputTypeEnum.Enum {
|
||||
if strings.HasSuffix(fileName, "."+suffix) {
|
||||
fileName = strings.TrimSuffix(fileName, "."+suffix)
|
||||
break
|
||||
}
|
||||
}
|
||||
fileName = strings.TrimSuffix(fileName, path.Ext(fileName))
|
||||
fileName += "." + outType
|
||||
}
|
||||
setting.LoadFromExisting()
|
||||
|
||||
setting.NewContext()
|
||||
// make sure we are logging to the console no matter what the configuration tells us do to
|
||||
if _, err := setting.Cfg.Section("log").NewKey("MODE", "console"); err != nil {
|
||||
fatal("Setting logging mode to console failed: %v", err)
|
||||
|
@ -196,10 +178,7 @@ func runDump(ctx *cli.Context) error {
|
|||
}
|
||||
setting.NewServices() // cannot access session settings otherwise
|
||||
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
err := db.InitEngine(stdCtx)
|
||||
err := models.SetEngine()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -254,7 +233,13 @@ func runDump(ctx *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
return addReader(w, object, info, path.Join("data", "lfs", objPath), verbose)
|
||||
return w.Write(archiver.File{
|
||||
FileInfo: archiver.FileInfo{
|
||||
FileInfo: info,
|
||||
CustomName: path.Join("data", "lfs", objPath),
|
||||
},
|
||||
ReadCloser: object,
|
||||
})
|
||||
}); err != nil {
|
||||
fatal("Failed to dump LFS objects: %v", err)
|
||||
}
|
||||
|
@ -265,7 +250,7 @@ func runDump(ctx *cli.Context) error {
|
|||
fatal("Path does not exist: %s", tmpDir)
|
||||
}
|
||||
|
||||
dbDump, err := os.CreateTemp(tmpDir, "gitea-db.sql")
|
||||
dbDump, err := ioutil.TempFile(tmpDir, "gitea-db.sql")
|
||||
if err != nil {
|
||||
fatal("Failed to create tmp file: %v", err)
|
||||
}
|
||||
|
@ -282,7 +267,7 @@ func runDump(ctx *cli.Context) error {
|
|||
log.Info("Dumping database...")
|
||||
}
|
||||
|
||||
if err := db.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
|
||||
if err := models.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
|
||||
fatal("Failed to dump database: %v", err)
|
||||
}
|
||||
|
||||
|
@ -324,21 +309,16 @@ func runDump(ctx *cli.Context) error {
|
|||
var excludes []string
|
||||
if setting.Cfg.Section("session").Key("PROVIDER").Value() == "file" {
|
||||
var opts session.Options
|
||||
json := jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
if err = json.Unmarshal([]byte(setting.SessionConfig.ProviderConfig), &opts); err != nil {
|
||||
return err
|
||||
}
|
||||
excludes = append(excludes, opts.ProviderConfig)
|
||||
}
|
||||
|
||||
if ctx.IsSet("skip-index") && ctx.Bool("skip-index") {
|
||||
excludes = append(excludes, setting.Indexer.RepoPath)
|
||||
excludes = append(excludes, setting.Indexer.IssuePath)
|
||||
}
|
||||
|
||||
excludes = append(excludes, setting.RepoRootPath)
|
||||
excludes = append(excludes, setting.LFS.Path)
|
||||
excludes = append(excludes, setting.Attachment.Path)
|
||||
excludes = append(excludes, setting.Packages.Path)
|
||||
excludes = append(excludes, setting.LogRootPath)
|
||||
excludes = append(excludes, absFileName)
|
||||
if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil {
|
||||
|
@ -354,24 +334,17 @@ func runDump(ctx *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
return addReader(w, object, info, path.Join("data", "attachments", objPath), verbose)
|
||||
return w.Write(archiver.File{
|
||||
FileInfo: archiver.FileInfo{
|
||||
FileInfo: info,
|
||||
CustomName: path.Join("data", "attachments", objPath),
|
||||
},
|
||||
ReadCloser: object,
|
||||
})
|
||||
}); err != nil {
|
||||
fatal("Failed to dump attachments: %v", err)
|
||||
}
|
||||
|
||||
if ctx.IsSet("skip-package-data") && ctx.Bool("skip-package-data") {
|
||||
log.Info("Skip dumping package data")
|
||||
} else if err := storage.Packages.IterateObjects(func(objPath string, object storage.Object) error {
|
||||
info, err := object.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return addReader(w, object, info, path.Join("data", "packages", objPath), verbose)
|
||||
}); err != nil {
|
||||
fatal("Failed to dump packages: %v", err)
|
||||
}
|
||||
|
||||
// Doesn't check if LogRootPath exists before processing --skip-log intentionally,
|
||||
// ensuring that it's clear the dump is skipped whether the directory's initialized
|
||||
// yet or not.
|
||||
|
@ -395,7 +368,7 @@ func runDump(ctx *cli.Context) error {
|
|||
fatal("Failed to save %s: %v", fileName, err)
|
||||
}
|
||||
|
||||
if err := os.Chmod(fileName, 0o600); err != nil {
|
||||
if err := os.Chmod(fileName, 0600); err != nil {
|
||||
log.Info("Can't change file access permissions mask to 0600: %v", err)
|
||||
}
|
||||
}
|
||||
|
@ -447,23 +420,8 @@ func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeA
|
|||
}
|
||||
}
|
||||
} 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
|
||||
}
|
||||
if err = addFile(w, currentInsidePath, currentAbsPath, verbose); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,20 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/convert"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
base "code.gitea.io/gitea/modules/migration"
|
||||
"code.gitea.io/gitea/modules/migrations"
|
||||
"code.gitea.io/gitea/modules/migrations/base"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/migrations"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
@ -79,15 +76,7 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme
|
|||
}
|
||||
|
||||
func runDumpRepository(ctx *cli.Context) error {
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(stdCtx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// migrations.GiteaLocalUploader depends on git module
|
||||
if err := git.InitSimple(context.Background()); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -95,7 +84,7 @@ func runDumpRepository(ctx *cli.Context) error {
|
|||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Info("Custom path: %s", setting.CustomPath)
|
||||
log.Info("Log path: %s", setting.LogRootPath)
|
||||
log.Info("Configuration file: %s", setting.CustomConf)
|
||||
setting.InitDBConfig()
|
||||
|
||||
var (
|
||||
serviceType structs.GitServiceType
|
||||
|
@ -115,7 +104,7 @@ func runDumpRepository(ctx *cli.Context) error {
|
|||
}
|
||||
serviceType = convert.ToGitServiceType(serviceStr)
|
||||
|
||||
opts := base.MigrateOptions{
|
||||
var opts = base.MigrateOptions{
|
||||
GitServiceType: serviceType,
|
||||
CloneAddr: cloneAddr,
|
||||
AuthUsername: ctx.String("auth_username"),
|
||||
|
@ -136,9 +125,7 @@ func runDumpRepository(ctx *cli.Context) error {
|
|||
} else {
|
||||
units := strings.Split(ctx.String("units"), ",")
|
||||
for _, unit := range units {
|
||||
switch strings.ToLower(strings.TrimSpace(unit)) {
|
||||
case "":
|
||||
continue
|
||||
switch strings.ToLower(unit) {
|
||||
case "wiki":
|
||||
opts.Wiki = true
|
||||
case "issues":
|
||||
|
@ -155,29 +142,13 @@ func runDumpRepository(ctx *cli.Context) error {
|
|||
opts.Comments = true
|
||||
case "pull_requests":
|
||||
opts.PullRequests = true
|
||||
default:
|
||||
return errors.New("invalid unit: " + unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the repo_dir will be removed if error occurs in DumpRepository
|
||||
// make sure the directory doesn't exist or is empty, prevent from deleting user files
|
||||
repoDir := ctx.String("repo_dir")
|
||||
if exists, err := util.IsExist(repoDir); err != nil {
|
||||
return fmt.Errorf("unable to stat repo_dir %q: %w", repoDir, err)
|
||||
} else if exists {
|
||||
if isDir, _ := util.IsDir(repoDir); !isDir {
|
||||
return fmt.Errorf("repo_dir %q already exists but it's not a directory", repoDir)
|
||||
}
|
||||
if dir, _ := os.ReadDir(repoDir); len(dir) > 0 {
|
||||
return fmt.Errorf("repo_dir %q is not empty", repoDir)
|
||||
}
|
||||
}
|
||||
|
||||
if err := migrations.DumpRepository(
|
||||
context.Background(),
|
||||
repoDir,
|
||||
ctx.String("repo_dir"),
|
||||
ctx.String("owner_name"),
|
||||
opts,
|
||||
); err != nil {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build bindata
|
||||
// +build bindata
|
||||
|
||||
package cmd
|
||||
|
||||
|
@ -107,12 +109,13 @@ type asset struct {
|
|||
}
|
||||
|
||||
func initEmbeddedExtractor(c *cli.Context) error {
|
||||
|
||||
// Silence the console logger
|
||||
log.DelNamedLogger("console")
|
||||
log.DelNamedLogger(log.DEFAULT)
|
||||
|
||||
// Read configuration file
|
||||
setting.LoadAllowEmpty()
|
||||
setting.NewContext()
|
||||
|
||||
pats, err := getPatterns(c.Args())
|
||||
if err != nil {
|
||||
|
@ -122,7 +125,7 @@ func initEmbeddedExtractor(c *cli.Context) error {
|
|||
|
||||
sections["public"] = §ion{Path: "public", Names: public.AssetNames, IsDir: public.AssetIsDir, Asset: public.Asset}
|
||||
sections["options"] = §ion{Path: "options", Names: options.AssetNames, IsDir: options.AssetIsDir, Asset: options.Asset}
|
||||
sections["templates"] = §ion{Path: "templates", Names: templates.BuiltinAssetNames, IsDir: templates.BuiltinAssetIsDir, Asset: templates.BuiltinAsset}
|
||||
sections["templates"] = §ion{Path: "templates", Names: templates.AssetNames, IsDir: templates.AssetIsDir, Asset: templates.Asset}
|
||||
|
||||
for _, sec := range sections {
|
||||
assets = append(assets, buildAssetList(sec, pats, c)...)
|
||||
|
@ -185,11 +188,11 @@ func runViewDo(c *cli.Context) error {
|
|||
|
||||
data, err := assets[0].Section.Asset(assets[0].Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", assets[0].Path, err)
|
||||
return fmt.Errorf("%s: %v", assets[0].Path, err)
|
||||
}
|
||||
|
||||
if _, err = os.Stdout.Write(data); err != nil {
|
||||
return fmt.Errorf("%s: %w", assets[0].Path, err)
|
||||
return fmt.Errorf("%s: %v", assets[0].Path, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -250,19 +253,19 @@ func extractAsset(d string, a asset, overwrite, rename bool) error {
|
|||
|
||||
data, err := a.Section.Asset(a.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", a.Path, err)
|
||||
return fmt.Errorf("%s: %v", a.Path, err)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("%s: %w", dir, err)
|
||||
return fmt.Errorf("%s: %v", dir, err)
|
||||
}
|
||||
|
||||
perms := os.ModePerm & 0o666
|
||||
perms := os.ModePerm & 0666
|
||||
|
||||
fi, err := os.Lstat(dest)
|
||||
if err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
return fmt.Errorf("%s: %w", dest, err)
|
||||
return fmt.Errorf("%s: %v", dest, err)
|
||||
}
|
||||
} else if !overwrite && !rename {
|
||||
fmt.Printf("%s already exists; skipped.\n", dest)
|
||||
|
@ -271,7 +274,7 @@ func extractAsset(d string, a asset, overwrite, rename bool) error {
|
|||
return fmt.Errorf("%s already exists, but it's not a regular file", dest)
|
||||
} else if rename {
|
||||
if err := util.Rename(dest, dest+".bak"); err != nil {
|
||||
return fmt.Errorf("Error creating backup for %s: %w", dest, err)
|
||||
return fmt.Errorf("Error creating backup for %s: %v", dest, err)
|
||||
}
|
||||
// Attempt to respect file permissions mask (even if user:group will be set anew)
|
||||
perms = fi.Mode()
|
||||
|
@ -279,12 +282,12 @@ func extractAsset(d string, a asset, overwrite, rename bool) error {
|
|||
|
||||
file, err := os.OpenFile(dest, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, perms)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", dest, err)
|
||||
return fmt.Errorf("%s: %v", dest, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if _, err = file.Write(data); err != nil {
|
||||
return fmt.Errorf("%s: %w", dest, err)
|
||||
return fmt.Errorf("%s: %v", dest, err)
|
||||
}
|
||||
|
||||
fmt.Println(dest)
|
||||
|
@ -293,7 +296,7 @@ func extractAsset(d string, a asset, overwrite, rename bool) error {
|
|||
}
|
||||
|
||||
func buildAssetList(sec *section, globs []glob.Glob, c *cli.Context) []asset {
|
||||
results := make([]asset, 0, 64)
|
||||
var results = make([]asset, 0, 64)
|
||||
for _, name := range sec.Names() {
|
||||
if isdir, err := sec.IsDir(name); !isdir && err == nil {
|
||||
if sec.Path == "public" &&
|
||||
|
@ -304,11 +307,9 @@ func buildAssetList(sec *section, globs []glob.Glob, c *cli.Context) []asset {
|
|||
matchName := sec.Path + "/" + name
|
||||
for _, g := range globs {
|
||||
if g.Match(matchName) {
|
||||
results = append(results, asset{
|
||||
Section: sec,
|
||||
Name: name,
|
||||
Path: sec.Path + "/" + name,
|
||||
})
|
||||
results = append(results, asset{Section: sec,
|
||||
Name: name,
|
||||
Path: sec.Path + "/" + name})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -324,7 +325,7 @@ func getPatterns(args []string) ([]glob.Glob, error) {
|
|||
pat := make([]glob.Glob, len(args))
|
||||
for i := range args {
|
||||
if g, err := glob.Compile(args[i], '/'); err != nil {
|
||||
return nil, fmt.Errorf("'%s': Invalid glob pattern: %w", args[i], err)
|
||||
return nil, fmt.Errorf("'%s': Invalid glob pattern: %v", args[i], err)
|
||||
} else {
|
||||
pat[i] = g
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !bindata
|
||||
// +build !bindata
|
||||
|
||||
package cmd
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
|
|
388
cmd/hook.go
388
cmd/hook.go
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
|
@ -14,9 +15,9 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
|
@ -37,7 +38,6 @@ var (
|
|||
subcmdHookPreReceive,
|
||||
subcmdHookUpdate,
|
||||
subcmdHookPostReceive,
|
||||
subcmdHookProcReceive,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -74,18 +74,6 @@ var (
|
|||
},
|
||||
},
|
||||
}
|
||||
// Note: new hook since git 2.29
|
||||
subcmdHookProcReceive = cli.Command{
|
||||
Name: "proc-receive",
|
||||
Usage: "Delegate proc-receive Git hook",
|
||||
Description: "This command should only be called by Git",
|
||||
Action: runHookProcReceive,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type delayWriter struct {
|
||||
|
@ -161,7 +149,7 @@ func (n *nilWriter) WriteString(s string) (int, error) {
|
|||
}
|
||||
|
||||
func runHookPreReceive(c *cli.Context) error {
|
||||
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
|
||||
if os.Getenv(models.EnvIsInternal) == "true" {
|
||||
return nil
|
||||
}
|
||||
ctx, cancel := installSignals()
|
||||
|
@ -179,12 +167,12 @@ Gitea or set your environment appropriately.`, "")
|
|||
}
|
||||
|
||||
// the environment is set by serv command
|
||||
isWiki, _ := strconv.ParseBool(os.Getenv(repo_module.EnvRepoIsWiki))
|
||||
username := os.Getenv(repo_module.EnvRepoUsername)
|
||||
reponame := os.Getenv(repo_module.EnvRepoName)
|
||||
userID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
|
||||
prID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPRID), 10, 64)
|
||||
deployKeyID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvDeployKeyID), 10, 64)
|
||||
isWiki := os.Getenv(models.EnvRepoIsWiki) == "true"
|
||||
username := os.Getenv(models.EnvRepoUsername)
|
||||
reponame := os.Getenv(models.EnvRepoName)
|
||||
userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||
prID, _ := strconv.ParseInt(os.Getenv(models.EnvPRID), 10, 64)
|
||||
isDeployKey, _ := strconv.ParseBool(os.Getenv(models.EnvIsDeployKey))
|
||||
|
||||
hookOptions := private.HookOptions{
|
||||
UserID: userID,
|
||||
|
@ -193,7 +181,7 @@ Gitea or set your environment appropriately.`, "")
|
|||
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
||||
GitPushOptions: pushOptions(),
|
||||
PullRequestID: prID,
|
||||
DeployKeyID: deployKeyID,
|
||||
IsDeployKey: isDeployKey,
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
|
@ -217,11 +205,6 @@ Gitea or set your environment appropriately.`, "")
|
|||
}
|
||||
}
|
||||
|
||||
supportProcReceive := false
|
||||
if git.CheckGitVersionAtLeast("2.29") == nil {
|
||||
supportProcReceive = true
|
||||
}
|
||||
|
||||
for scanner.Scan() {
|
||||
// TODO: support news feeds for wiki
|
||||
if isWiki {
|
||||
|
@ -240,9 +223,7 @@ Gitea or set your environment appropriately.`, "")
|
|||
lastline++
|
||||
|
||||
// If the ref is a branch or tag, check if it's protected
|
||||
// if supportProcReceive all ref should be checked because
|
||||
// permission check was delayed
|
||||
if supportProcReceive || strings.HasPrefix(refFullName, git.BranchPrefix) || strings.HasPrefix(refFullName, git.TagPrefix) {
|
||||
if strings.HasPrefix(refFullName, git.BranchPrefix) || strings.HasPrefix(refFullName, git.TagPrefix) {
|
||||
oldCommitIDs[count] = oldCommitID
|
||||
newCommitIDs[count] = newCommitID
|
||||
refFullNames[count] = refFullName
|
||||
|
@ -292,6 +273,7 @@ Gitea or set your environment appropriately.`, "")
|
|||
}
|
||||
} else if lastline > 0 {
|
||||
fmt.Fprintf(out, "\n")
|
||||
lastline = 0
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "Checked %d references in total\n", total)
|
||||
|
@ -307,18 +289,18 @@ func runHookPostReceive(c *cli.Context) error {
|
|||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setup("hooks/post-receive.log", c.Bool("debug"))
|
||||
|
||||
// First of all run update-server-info no matter what
|
||||
if _, _, err := git.NewCommand(ctx, "update-server-info").RunStdString(nil); err != nil {
|
||||
return fmt.Errorf("Failed to call 'git update-server-info': %w", err)
|
||||
if _, err := git.NewCommand("update-server-info").SetParentContext(ctx).Run(); err != nil {
|
||||
return fmt.Errorf("Failed to call 'git update-server-info': %v", err)
|
||||
}
|
||||
|
||||
// Now if we're an internal don't do anything else
|
||||
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
|
||||
if os.Getenv(models.EnvIsInternal) == "true" {
|
||||
return nil
|
||||
}
|
||||
|
||||
setup("hooks/post-receive.log", c.Bool("debug"))
|
||||
|
||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
||||
return fail(`Rejecting changes as Gitea environment not set.
|
||||
|
@ -342,11 +324,11 @@ Gitea or set your environment appropriately.`, "")
|
|||
}
|
||||
|
||||
// the environment is set by serv command
|
||||
repoUser := os.Getenv(repo_module.EnvRepoUsername)
|
||||
isWiki, _ := strconv.ParseBool(os.Getenv(repo_module.EnvRepoIsWiki))
|
||||
repoName := os.Getenv(repo_module.EnvRepoName)
|
||||
pusherID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
|
||||
pusherName := os.Getenv(repo_module.EnvPusherName)
|
||||
repoUser := os.Getenv(models.EnvRepoUsername)
|
||||
isWiki := os.Getenv(models.EnvRepoIsWiki) == "true"
|
||||
repoName := os.Getenv(models.EnvRepoName)
|
||||
pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||
pusherName := os.Getenv(models.EnvPusherName)
|
||||
|
||||
hookOptions := private.HookOptions{
|
||||
UserName: pusherName,
|
||||
|
@ -481,327 +463,3 @@ func pushOptions() map[string]string {
|
|||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
func runHookProcReceive(c *cli.Context) error {
|
||||
setup("hooks/proc-receive.log", c.Bool("debug"))
|
||||
|
||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
||||
return fail(`Rejecting changes as Gitea environment not set.
|
||||
If you are pushing over SSH you must push with a key managed by
|
||||
Gitea or set your environment appropriately.`, "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if git.CheckGitVersionAtLeast("2.29") != nil {
|
||||
return fail("Internal Server Error", "git not support proc-receive.")
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
repoUser := os.Getenv(repo_module.EnvRepoUsername)
|
||||
repoName := os.Getenv(repo_module.EnvRepoName)
|
||||
pusherID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
|
||||
pusherName := os.Getenv(repo_module.EnvPusherName)
|
||||
|
||||
// 1. Version and features negotiation.
|
||||
// S: PKT-LINE(version=1\0push-options atomic...) / PKT-LINE(version=1\n)
|
||||
// S: flush-pkt
|
||||
// H: PKT-LINE(version=1\0push-options...)
|
||||
// H: flush-pkt
|
||||
|
||||
rs, err := readPktLine(reader, pktLineTypeData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
const VersionHead string = "version=1"
|
||||
|
||||
var (
|
||||
hasPushOptions bool
|
||||
response = []byte(VersionHead)
|
||||
requestOptions []string
|
||||
)
|
||||
|
||||
index := bytes.IndexByte(rs.Data, byte(0))
|
||||
if index >= len(rs.Data) {
|
||||
return fail("Internal Server Error", "pkt-line: format error "+fmt.Sprint(rs.Data))
|
||||
}
|
||||
|
||||
if index < 0 {
|
||||
if len(rs.Data) == 10 && rs.Data[9] == '\n' {
|
||||
index = 9
|
||||
} else {
|
||||
return fail("Internal Server Error", "pkt-line: format error "+fmt.Sprint(rs.Data))
|
||||
}
|
||||
}
|
||||
|
||||
if string(rs.Data[0:index]) != VersionHead {
|
||||
return fail("Internal Server Error", "Received unsupported version: %s", string(rs.Data[0:index]))
|
||||
}
|
||||
requestOptions = strings.Split(string(rs.Data[index+1:]), " ")
|
||||
|
||||
for _, option := range requestOptions {
|
||||
if strings.HasPrefix(option, "push-options") {
|
||||
response = append(response, byte(0))
|
||||
response = append(response, []byte("push-options")...)
|
||||
hasPushOptions = true
|
||||
}
|
||||
}
|
||||
response = append(response, '\n')
|
||||
|
||||
_, err = readPktLine(reader, pktLineTypeFlush)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = writeDataPktLine(os.Stdout, response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = writeFlushPktLine(os.Stdout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. receive commands from server.
|
||||
// S: PKT-LINE(<old-oid> <new-oid> <ref>)
|
||||
// S: ... ...
|
||||
// S: flush-pkt
|
||||
// # [receive push-options]
|
||||
// S: PKT-LINE(push-option)
|
||||
// S: ... ...
|
||||
// S: flush-pkt
|
||||
hookOptions := private.HookOptions{
|
||||
UserName: pusherName,
|
||||
UserID: pusherID,
|
||||
}
|
||||
hookOptions.OldCommitIDs = make([]string, 0, hookBatchSize)
|
||||
hookOptions.NewCommitIDs = make([]string, 0, hookBatchSize)
|
||||
hookOptions.RefFullNames = make([]string, 0, hookBatchSize)
|
||||
|
||||
for {
|
||||
// note: pktLineTypeUnknow means pktLineTypeFlush and pktLineTypeData all allowed
|
||||
rs, err = readPktLine(reader, pktLineTypeUnknow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rs.Type == pktLineTypeFlush {
|
||||
break
|
||||
}
|
||||
t := strings.SplitN(string(rs.Data), " ", 3)
|
||||
if len(t) != 3 {
|
||||
continue
|
||||
}
|
||||
hookOptions.OldCommitIDs = append(hookOptions.OldCommitIDs, t[0])
|
||||
hookOptions.NewCommitIDs = append(hookOptions.NewCommitIDs, t[1])
|
||||
hookOptions.RefFullNames = append(hookOptions.RefFullNames, t[2])
|
||||
}
|
||||
|
||||
hookOptions.GitPushOptions = make(map[string]string)
|
||||
|
||||
if hasPushOptions {
|
||||
for {
|
||||
rs, err = readPktLine(reader, pktLineTypeUnknow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rs.Type == pktLineTypeFlush {
|
||||
break
|
||||
}
|
||||
|
||||
kv := strings.SplitN(string(rs.Data), "=", 2)
|
||||
if len(kv) == 2 {
|
||||
hookOptions.GitPushOptions[kv[0]] = kv[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. run hook
|
||||
resp, err := private.HookProcReceive(ctx, repoUser, repoName, hookOptions)
|
||||
if err != nil {
|
||||
return fail("Internal Server Error", "run proc-receive hook failed :%v", err)
|
||||
}
|
||||
|
||||
// 4. response result to service
|
||||
// # a. OK, but has an alternate reference. The alternate reference name
|
||||
// # and other status can be given in option directives.
|
||||
// H: PKT-LINE(ok <ref>)
|
||||
// H: PKT-LINE(option refname <refname>)
|
||||
// H: PKT-LINE(option old-oid <old-oid>)
|
||||
// H: PKT-LINE(option new-oid <new-oid>)
|
||||
// H: PKT-LINE(option forced-update)
|
||||
// H: ... ...
|
||||
// H: flush-pkt
|
||||
// # b. NO, I reject it.
|
||||
// H: PKT-LINE(ng <ref> <reason>)
|
||||
// # c. Fall through, let 'receive-pack' to execute it.
|
||||
// H: PKT-LINE(ok <ref>)
|
||||
// H: PKT-LINE(option fall-through)
|
||||
|
||||
for _, rs := range resp.Results {
|
||||
if len(rs.Err) > 0 {
|
||||
err = writeDataPktLine(os.Stdout, []byte("ng "+rs.OriginalRef+" "+rs.Err))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if rs.IsNotMatched {
|
||||
err = writeDataPktLine(os.Stdout, []byte("ok "+rs.OriginalRef))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = writeDataPktLine(os.Stdout, []byte("option fall-through"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
err = writeDataPktLine(os.Stdout, []byte("ok "+rs.OriginalRef))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = writeDataPktLine(os.Stdout, []byte("option refname "+rs.Ref))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rs.OldOID != git.EmptySHA {
|
||||
err = writeDataPktLine(os.Stdout, []byte("option old-oid "+rs.OldOID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = writeDataPktLine(os.Stdout, []byte("option new-oid "+rs.NewOID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rs.IsForcePush {
|
||||
err = writeDataPktLine(os.Stdout, []byte("option forced-update"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
err = writeFlushPktLine(os.Stdout)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// git PKT-Line api
|
||||
// pktLineType message type of pkt-line
|
||||
type pktLineType int64
|
||||
|
||||
const (
|
||||
// UnKnow type
|
||||
pktLineTypeUnknow pktLineType = 0
|
||||
// flush-pkt "0000"
|
||||
pktLineTypeFlush pktLineType = iota
|
||||
// data line
|
||||
pktLineTypeData
|
||||
)
|
||||
|
||||
// gitPktLine pkt-line api
|
||||
type gitPktLine struct {
|
||||
Type pktLineType
|
||||
Length uint64
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func readPktLine(in *bufio.Reader, requestType pktLineType) (*gitPktLine, error) {
|
||||
var (
|
||||
err error
|
||||
r *gitPktLine
|
||||
)
|
||||
|
||||
// read prefix
|
||||
lengthBytes := make([]byte, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
lengthBytes[i], err = in.ReadByte()
|
||||
if err != nil {
|
||||
return nil, fail("Internal Server Error", "Pkt-Line: read stdin failed : %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
r = new(gitPktLine)
|
||||
r.Length, err = strconv.ParseUint(string(lengthBytes), 16, 32)
|
||||
if err != nil {
|
||||
return nil, fail("Internal Server Error", "Pkt-Line format is wrong :%v", err)
|
||||
}
|
||||
|
||||
if r.Length == 0 {
|
||||
if requestType == pktLineTypeData {
|
||||
return nil, fail("Internal Server Error", "Pkt-Line format is wrong")
|
||||
}
|
||||
r.Type = pktLineTypeFlush
|
||||
return r, nil
|
||||
}
|
||||
|
||||
if r.Length <= 4 || r.Length > 65520 || requestType == pktLineTypeFlush {
|
||||
return nil, fail("Internal Server Error", "Pkt-Line format is wrong")
|
||||
}
|
||||
|
||||
r.Data = make([]byte, r.Length-4)
|
||||
for i := range r.Data {
|
||||
r.Data[i], err = in.ReadByte()
|
||||
if err != nil {
|
||||
return nil, fail("Internal Server Error", "Pkt-Line: read stdin failed : %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
r.Type = pktLineTypeData
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func writeFlushPktLine(out io.Writer) error {
|
||||
l, err := out.Write([]byte("0000"))
|
||||
if err != nil {
|
||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
||||
}
|
||||
if l != 4 {
|
||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeDataPktLine(out io.Writer, data []byte) error {
|
||||
hexchar := []byte("0123456789abcdef")
|
||||
hex := func(n uint64) byte {
|
||||
return hexchar[(n)&15]
|
||||
}
|
||||
|
||||
length := uint64(len(data) + 4)
|
||||
tmp := make([]byte, 4)
|
||||
tmp[0] = hex(length >> 12)
|
||||
tmp[1] = hex(length >> 8)
|
||||
tmp[2] = hex(length >> 4)
|
||||
tmp[3] = hex(length)
|
||||
|
||||
lr, err := out.Write(tmp)
|
||||
if err != nil {
|
||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
||||
}
|
||||
if lr != 4 {
|
||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
||||
}
|
||||
|
||||
lr, err = out.Write(data)
|
||||
if err != nil {
|
||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
||||
}
|
||||
if int(length-4) != lr {
|
||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPktLine(t *testing.T) {
|
||||
// test read
|
||||
s := strings.NewReader("0000")
|
||||
r := bufio.NewReader(s)
|
||||
result, err := readPktLine(r, pktLineTypeFlush)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, pktLineTypeFlush, result.Type)
|
||||
|
||||
s = strings.NewReader("0006a\n")
|
||||
r = bufio.NewReader(s)
|
||||
result, err = readPktLine(r, pktLineTypeData)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, pktLineTypeData, result.Type)
|
||||
assert.Equal(t, []byte("a\n"), result.Data)
|
||||
|
||||
// test write
|
||||
w := bytes.NewBuffer([]byte{})
|
||||
err = writeFlushPktLine(w)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []byte("0000"), w.Bytes())
|
||||
|
||||
w.Reset()
|
||||
err = writeDataPktLine(w, []byte("a\nb"))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []byte("0007a\nb"), w.Bytes())
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
|
@ -9,7 +10,6 @@ import (
|
|||
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
|
@ -17,7 +17,7 @@ func runSendMail(c *cli.Context) error {
|
|||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setting.LoadFromExisting()
|
||||
setting.NewContext()
|
||||
|
||||
if err := argsSet(c, "title"); err != nil {
|
||||
return err
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
func init() {
|
||||
setting.SetCustomPathAndConf("", "", "")
|
||||
setting.LoadForTest()
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m, &unittest.TestOptions{
|
||||
GiteaRootPath: "..",
|
||||
})
|
||||
}
|
385
cmd/manager.go
385
cmd/manager.go
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
|
@ -9,6 +10,7 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
|
@ -25,7 +27,6 @@ var (
|
|||
subcmdRestart,
|
||||
subcmdFlushQueues,
|
||||
subcmdLogging,
|
||||
subCmdProcesses,
|
||||
},
|
||||
}
|
||||
subcmdShutdown = cli.Command{
|
||||
|
@ -57,8 +58,7 @@ var (
|
|||
Name: "timeout",
|
||||
Value: 60 * time.Second,
|
||||
Usage: "Timeout for the flushing process",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
}, cli.BoolFlag{
|
||||
Name: "non-blocking",
|
||||
Usage: "Set to true to not wait for flush to complete before returning",
|
||||
},
|
||||
|
@ -67,38 +67,326 @@ var (
|
|||
},
|
||||
},
|
||||
}
|
||||
subCmdProcesses = cli.Command{
|
||||
Name: "processes",
|
||||
Usage: "Display running processes within the current process",
|
||||
Action: runProcesses,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "flat",
|
||||
Usage: "Show processes as flat table rather than as tree",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "no-system",
|
||||
Usage: "Do not show system processes",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "stacktraces",
|
||||
Usage: "Show stacktraces",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "json",
|
||||
Usage: "Output as json",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "cancel",
|
||||
Usage: "Process PID to cancel. (Only available for non-system processes.)",
|
||||
defaultLoggingFlags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "group, g",
|
||||
Usage: "Group to add logger to - will default to \"default\"",
|
||||
}, cli.StringFlag{
|
||||
Name: "name, n",
|
||||
Usage: "Name of the new logger - will default to mode",
|
||||
}, cli.StringFlag{
|
||||
Name: "level, l",
|
||||
Usage: "Logging level for the new logger",
|
||||
}, cli.StringFlag{
|
||||
Name: "stacktrace-level, L",
|
||||
Usage: "Stacktrace logging level",
|
||||
}, cli.StringFlag{
|
||||
Name: "flags, F",
|
||||
Usage: "Flags for the logger",
|
||||
}, cli.StringFlag{
|
||||
Name: "expression, e",
|
||||
Usage: "Matching expression for the logger",
|
||||
}, cli.StringFlag{
|
||||
Name: "prefix, p",
|
||||
Usage: "Prefix for the logger",
|
||||
}, cli.BoolFlag{
|
||||
Name: "color",
|
||||
Usage: "Use color in the logs",
|
||||
}, cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
}
|
||||
subcmdLogging = cli.Command{
|
||||
Name: "logging",
|
||||
Usage: "Adjust logging commands",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "pause",
|
||||
Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
Action: runPauseLogging,
|
||||
}, {
|
||||
Name: "resume",
|
||||
Usage: "Resume logging",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
Action: runResumeLogging,
|
||||
}, {
|
||||
Name: "release-and-reopen",
|
||||
Usage: "Cause Gitea to release and re-open files used for logging",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
Action: runReleaseReopenLogging,
|
||||
}, {
|
||||
Name: "remove",
|
||||
Usage: "Remove a logger",
|
||||
ArgsUsage: "[name] Name of logger to remove",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
}, cli.StringFlag{
|
||||
Name: "group, g",
|
||||
Usage: "Group to add logger to - will default to \"default\"",
|
||||
},
|
||||
},
|
||||
Action: runRemoveLogger,
|
||||
}, {
|
||||
Name: "add",
|
||||
Usage: "Add a logger",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "console",
|
||||
Usage: "Add a console logger",
|
||||
Flags: append(defaultLoggingFlags,
|
||||
cli.BoolFlag{
|
||||
Name: "stderr",
|
||||
Usage: "Output console logs to stderr - only relevant for console",
|
||||
}),
|
||||
Action: runAddConsoleLogger,
|
||||
}, {
|
||||
Name: "file",
|
||||
Usage: "Add a file logger",
|
||||
Flags: append(defaultLoggingFlags, []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "filename, f",
|
||||
Usage: "Filename for the logger - this must be set.",
|
||||
}, cli.BoolTFlag{
|
||||
Name: "rotate, r",
|
||||
Usage: "Rotate logs",
|
||||
}, cli.Int64Flag{
|
||||
Name: "max-size, s",
|
||||
Usage: "Maximum size in bytes before rotation",
|
||||
}, cli.BoolTFlag{
|
||||
Name: "daily, d",
|
||||
Usage: "Rotate logs daily",
|
||||
}, cli.IntFlag{
|
||||
Name: "max-days, D",
|
||||
Usage: "Maximum number of daily logs to keep",
|
||||
}, cli.BoolTFlag{
|
||||
Name: "compress, z",
|
||||
Usage: "Compress rotated logs",
|
||||
}, cli.IntFlag{
|
||||
Name: "compression-level, Z",
|
||||
Usage: "Compression level to use",
|
||||
},
|
||||
}...),
|
||||
Action: runAddFileLogger,
|
||||
}, {
|
||||
Name: "conn",
|
||||
Usage: "Add a net conn logger",
|
||||
Flags: append(defaultLoggingFlags, []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "reconnect-on-message, R",
|
||||
Usage: "Reconnect to host for every message",
|
||||
}, cli.BoolFlag{
|
||||
Name: "reconnect, r",
|
||||
Usage: "Reconnect to host when connection is dropped",
|
||||
}, cli.StringFlag{
|
||||
Name: "protocol, P",
|
||||
Usage: "Set protocol to use: tcp, unix, or udp (defaults to tcp)",
|
||||
}, cli.StringFlag{
|
||||
Name: "address, a",
|
||||
Usage: "Host address and port to connect to (defaults to :7020)",
|
||||
},
|
||||
}...),
|
||||
Action: runAddConnLogger,
|
||||
}, {
|
||||
Name: "smtp",
|
||||
Usage: "Add an SMTP logger",
|
||||
Flags: append(defaultLoggingFlags, []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "username, u",
|
||||
Usage: "Mail server username",
|
||||
}, cli.StringFlag{
|
||||
Name: "password, P",
|
||||
Usage: "Mail server password",
|
||||
}, cli.StringFlag{
|
||||
Name: "host, H",
|
||||
Usage: "Mail server host (defaults to: 127.0.0.1:25)",
|
||||
}, cli.StringSliceFlag{
|
||||
Name: "send-to, s",
|
||||
Usage: "Email address(es) to send to",
|
||||
}, cli.StringFlag{
|
||||
Name: "subject, S",
|
||||
Usage: "Subject header of sent emails",
|
||||
},
|
||||
}...),
|
||||
Action: runAddSMTPLogger,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func runRemoveLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
group := c.String("group")
|
||||
if len(group) == 0 {
|
||||
group = log.DEFAULT
|
||||
}
|
||||
name := c.Args().First()
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
statusCode, msg := private.RemoveLogger(ctx, group, name)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runAddSMTPLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
mode := "smtp"
|
||||
if c.IsSet("host") {
|
||||
vals["host"] = c.String("host")
|
||||
} else {
|
||||
vals["host"] = "127.0.0.1:25"
|
||||
}
|
||||
|
||||
if c.IsSet("username") {
|
||||
vals["username"] = c.String("username")
|
||||
}
|
||||
if c.IsSet("password") {
|
||||
vals["password"] = c.String("password")
|
||||
}
|
||||
|
||||
if !c.IsSet("send-to") {
|
||||
return fmt.Errorf("Some recipients must be provided")
|
||||
}
|
||||
vals["sendTos"] = c.StringSlice("send-to")
|
||||
|
||||
if c.IsSet("subject") {
|
||||
vals["subject"] = c.String("subject")
|
||||
} else {
|
||||
vals["subject"] = "Diagnostic message from Gitea"
|
||||
}
|
||||
|
||||
return commonAddLogger(c, mode, vals)
|
||||
}
|
||||
|
||||
func runAddConnLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
mode := "conn"
|
||||
vals["net"] = "tcp"
|
||||
if c.IsSet("protocol") {
|
||||
switch c.String("protocol") {
|
||||
case "udp":
|
||||
vals["net"] = "udp"
|
||||
case "unix":
|
||||
vals["net"] = "unix"
|
||||
}
|
||||
}
|
||||
if c.IsSet("address") {
|
||||
vals["address"] = c.String("address")
|
||||
} else {
|
||||
vals["address"] = ":7020"
|
||||
}
|
||||
if c.IsSet("reconnect") {
|
||||
vals["reconnect"] = c.Bool("reconnect")
|
||||
}
|
||||
if c.IsSet("reconnect-on-message") {
|
||||
vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
|
||||
}
|
||||
return commonAddLogger(c, mode, vals)
|
||||
}
|
||||
|
||||
func runAddFileLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
mode := "file"
|
||||
if c.IsSet("filename") {
|
||||
vals["filename"] = c.String("filename")
|
||||
} else {
|
||||
return fmt.Errorf("filename must be set when creating a file logger")
|
||||
}
|
||||
if c.IsSet("rotate") {
|
||||
vals["rotate"] = c.Bool("rotate")
|
||||
}
|
||||
if c.IsSet("max-size") {
|
||||
vals["maxsize"] = c.Int64("max-size")
|
||||
}
|
||||
if c.IsSet("daily") {
|
||||
vals["daily"] = c.Bool("daily")
|
||||
}
|
||||
if c.IsSet("max-days") {
|
||||
vals["maxdays"] = c.Int("max-days")
|
||||
}
|
||||
if c.IsSet("compress") {
|
||||
vals["compress"] = c.Bool("compress")
|
||||
}
|
||||
if c.IsSet("compression-level") {
|
||||
vals["compressionLevel"] = c.Int("compression-level")
|
||||
}
|
||||
return commonAddLogger(c, mode, vals)
|
||||
}
|
||||
|
||||
func runAddConsoleLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
mode := "console"
|
||||
if c.IsSet("stderr") && c.Bool("stderr") {
|
||||
vals["stderr"] = c.Bool("stderr")
|
||||
}
|
||||
return commonAddLogger(c, mode, vals)
|
||||
}
|
||||
|
||||
func commonAddLogger(c *cli.Context, mode string, vals map[string]interface{}) error {
|
||||
if len(c.String("level")) > 0 {
|
||||
vals["level"] = log.FromString(c.String("level")).String()
|
||||
}
|
||||
if len(c.String("stacktrace-level")) > 0 {
|
||||
vals["stacktraceLevel"] = log.FromString(c.String("stacktrace-level")).String()
|
||||
}
|
||||
if len(c.String("expression")) > 0 {
|
||||
vals["expression"] = c.String("expression")
|
||||
}
|
||||
if len(c.String("prefix")) > 0 {
|
||||
vals["prefix"] = c.String("prefix")
|
||||
}
|
||||
if len(c.String("flags")) > 0 {
|
||||
vals["flags"] = log.FlagsFromString(c.String("flags"))
|
||||
}
|
||||
if c.IsSet("color") {
|
||||
vals["colorize"] = c.Bool("color")
|
||||
}
|
||||
group := "default"
|
||||
if c.IsSet("group") {
|
||||
group = c.String("group")
|
||||
}
|
||||
name := mode
|
||||
if c.IsSet("name") {
|
||||
name = c.String("name")
|
||||
}
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
statusCode, msg := private.AddLogger(ctx, group, name, mode, vals)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runShutdown(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
@ -144,16 +432,47 @@ func runFlushQueues(c *cli.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func runProcesses(c *cli.Context) error {
|
||||
func runPauseLogging(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.Processes(ctx, os.Stdout, c.Bool("flat"), c.Bool("no-system"), c.Bool("stacktraces"), c.Bool("json"), c.String("cancel"))
|
||||
statusCode, msg := private.PauseLogging(ctx)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runResumeLogging(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.ResumeLogging(ctx)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runReleaseReopenLogging(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.ReleaseReopenLogging(ctx)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,409 +0,0 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultLoggingFlags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "group, g",
|
||||
Usage: "Group to add logger to - will default to \"default\"",
|
||||
}, cli.StringFlag{
|
||||
Name: "name, n",
|
||||
Usage: "Name of the new logger - will default to mode",
|
||||
}, cli.StringFlag{
|
||||
Name: "level, l",
|
||||
Usage: "Logging level for the new logger",
|
||||
}, cli.StringFlag{
|
||||
Name: "stacktrace-level, L",
|
||||
Usage: "Stacktrace logging level",
|
||||
}, cli.StringFlag{
|
||||
Name: "flags, F",
|
||||
Usage: "Flags for the logger",
|
||||
}, cli.StringFlag{
|
||||
Name: "expression, e",
|
||||
Usage: "Matching expression for the logger",
|
||||
}, cli.StringFlag{
|
||||
Name: "prefix, p",
|
||||
Usage: "Prefix for the logger",
|
||||
}, cli.BoolFlag{
|
||||
Name: "color",
|
||||
Usage: "Use color in the logs",
|
||||
}, cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
}
|
||||
|
||||
subcmdLogging = cli.Command{
|
||||
Name: "logging",
|
||||
Usage: "Adjust logging commands",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "pause",
|
||||
Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
Action: runPauseLogging,
|
||||
}, {
|
||||
Name: "resume",
|
||||
Usage: "Resume logging",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
Action: runResumeLogging,
|
||||
}, {
|
||||
Name: "release-and-reopen",
|
||||
Usage: "Cause Gitea to release and re-open files used for logging",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
Action: runReleaseReopenLogging,
|
||||
}, {
|
||||
Name: "remove",
|
||||
Usage: "Remove a logger",
|
||||
ArgsUsage: "[name] Name of logger to remove",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
}, cli.StringFlag{
|
||||
Name: "group, g",
|
||||
Usage: "Group to add logger to - will default to \"default\"",
|
||||
},
|
||||
},
|
||||
Action: runRemoveLogger,
|
||||
}, {
|
||||
Name: "add",
|
||||
Usage: "Add a logger",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "console",
|
||||
Usage: "Add a console logger",
|
||||
Flags: append(defaultLoggingFlags,
|
||||
cli.BoolFlag{
|
||||
Name: "stderr",
|
||||
Usage: "Output console logs to stderr - only relevant for console",
|
||||
}),
|
||||
Action: runAddConsoleLogger,
|
||||
}, {
|
||||
Name: "file",
|
||||
Usage: "Add a file logger",
|
||||
Flags: append(defaultLoggingFlags, []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "filename, f",
|
||||
Usage: "Filename for the logger - this must be set.",
|
||||
}, cli.BoolTFlag{
|
||||
Name: "rotate, r",
|
||||
Usage: "Rotate logs",
|
||||
}, cli.Int64Flag{
|
||||
Name: "max-size, s",
|
||||
Usage: "Maximum size in bytes before rotation",
|
||||
}, cli.BoolTFlag{
|
||||
Name: "daily, d",
|
||||
Usage: "Rotate logs daily",
|
||||
}, cli.IntFlag{
|
||||
Name: "max-days, D",
|
||||
Usage: "Maximum number of daily logs to keep",
|
||||
}, cli.BoolTFlag{
|
||||
Name: "compress, z",
|
||||
Usage: "Compress rotated logs",
|
||||
}, cli.IntFlag{
|
||||
Name: "compression-level, Z",
|
||||
Usage: "Compression level to use",
|
||||
},
|
||||
}...),
|
||||
Action: runAddFileLogger,
|
||||
}, {
|
||||
Name: "conn",
|
||||
Usage: "Add a net conn logger",
|
||||
Flags: append(defaultLoggingFlags, []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "reconnect-on-message, R",
|
||||
Usage: "Reconnect to host for every message",
|
||||
}, cli.BoolFlag{
|
||||
Name: "reconnect, r",
|
||||
Usage: "Reconnect to host when connection is dropped",
|
||||
}, cli.StringFlag{
|
||||
Name: "protocol, P",
|
||||
Usage: "Set protocol to use: tcp, unix, or udp (defaults to tcp)",
|
||||
}, cli.StringFlag{
|
||||
Name: "address, a",
|
||||
Usage: "Host address and port to connect to (defaults to :7020)",
|
||||
},
|
||||
}...),
|
||||
Action: runAddConnLogger,
|
||||
}, {
|
||||
Name: "smtp",
|
||||
Usage: "Add an SMTP logger",
|
||||
Flags: append(defaultLoggingFlags, []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "username, u",
|
||||
Usage: "Mail server username",
|
||||
}, cli.StringFlag{
|
||||
Name: "password, P",
|
||||
Usage: "Mail server password",
|
||||
}, cli.StringFlag{
|
||||
Name: "host, H",
|
||||
Usage: "Mail server host (defaults to: 127.0.0.1:25)",
|
||||
}, cli.StringSliceFlag{
|
||||
Name: "send-to, s",
|
||||
Usage: "Email address(es) to send to",
|
||||
}, cli.StringFlag{
|
||||
Name: "subject, S",
|
||||
Usage: "Subject header of sent emails",
|
||||
},
|
||||
}...),
|
||||
Action: runAddSMTPLogger,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Name: "log-sql",
|
||||
Usage: "Set LogSQL",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
}, cli.BoolFlag{
|
||||
Name: "off",
|
||||
Usage: "Switch off SQL logging",
|
||||
},
|
||||
},
|
||||
Action: runSetLogSQL,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func runRemoveLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
group := c.String("group")
|
||||
if len(group) == 0 {
|
||||
group = log.DEFAULT
|
||||
}
|
||||
name := c.Args().First()
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
statusCode, msg := private.RemoveLogger(ctx, group, name)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runAddSMTPLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
mode := "smtp"
|
||||
if c.IsSet("host") {
|
||||
vals["host"] = c.String("host")
|
||||
} else {
|
||||
vals["host"] = "127.0.0.1:25"
|
||||
}
|
||||
|
||||
if c.IsSet("username") {
|
||||
vals["username"] = c.String("username")
|
||||
}
|
||||
if c.IsSet("password") {
|
||||
vals["password"] = c.String("password")
|
||||
}
|
||||
|
||||
if !c.IsSet("send-to") {
|
||||
return fmt.Errorf("Some recipients must be provided")
|
||||
}
|
||||
vals["sendTos"] = c.StringSlice("send-to")
|
||||
|
||||
if c.IsSet("subject") {
|
||||
vals["subject"] = c.String("subject")
|
||||
} else {
|
||||
vals["subject"] = "Diagnostic message from Gitea"
|
||||
}
|
||||
|
||||
return commonAddLogger(c, mode, vals)
|
||||
}
|
||||
|
||||
func runAddConnLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
mode := "conn"
|
||||
vals["net"] = "tcp"
|
||||
if c.IsSet("protocol") {
|
||||
switch c.String("protocol") {
|
||||
case "udp":
|
||||
vals["net"] = "udp"
|
||||
case "unix":
|
||||
vals["net"] = "unix"
|
||||
}
|
||||
}
|
||||
if c.IsSet("address") {
|
||||
vals["address"] = c.String("address")
|
||||
} else {
|
||||
vals["address"] = ":7020"
|
||||
}
|
||||
if c.IsSet("reconnect") {
|
||||
vals["reconnect"] = c.Bool("reconnect")
|
||||
}
|
||||
if c.IsSet("reconnect-on-message") {
|
||||
vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
|
||||
}
|
||||
return commonAddLogger(c, mode, vals)
|
||||
}
|
||||
|
||||
func runAddFileLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
mode := "file"
|
||||
if c.IsSet("filename") {
|
||||
vals["filename"] = c.String("filename")
|
||||
} else {
|
||||
return fmt.Errorf("filename must be set when creating a file logger")
|
||||
}
|
||||
if c.IsSet("rotate") {
|
||||
vals["rotate"] = c.Bool("rotate")
|
||||
}
|
||||
if c.IsSet("max-size") {
|
||||
vals["maxsize"] = c.Int64("max-size")
|
||||
}
|
||||
if c.IsSet("daily") {
|
||||
vals["daily"] = c.Bool("daily")
|
||||
}
|
||||
if c.IsSet("max-days") {
|
||||
vals["maxdays"] = c.Int("max-days")
|
||||
}
|
||||
if c.IsSet("compress") {
|
||||
vals["compress"] = c.Bool("compress")
|
||||
}
|
||||
if c.IsSet("compression-level") {
|
||||
vals["compressionLevel"] = c.Int("compression-level")
|
||||
}
|
||||
return commonAddLogger(c, mode, vals)
|
||||
}
|
||||
|
||||
func runAddConsoleLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
mode := "console"
|
||||
if c.IsSet("stderr") && c.Bool("stderr") {
|
||||
vals["stderr"] = c.Bool("stderr")
|
||||
}
|
||||
return commonAddLogger(c, mode, vals)
|
||||
}
|
||||
|
||||
func commonAddLogger(c *cli.Context, mode string, vals map[string]interface{}) error {
|
||||
if len(c.String("level")) > 0 {
|
||||
vals["level"] = log.FromString(c.String("level")).String()
|
||||
}
|
||||
if len(c.String("stacktrace-level")) > 0 {
|
||||
vals["stacktraceLevel"] = log.FromString(c.String("stacktrace-level")).String()
|
||||
}
|
||||
if len(c.String("expression")) > 0 {
|
||||
vals["expression"] = c.String("expression")
|
||||
}
|
||||
if len(c.String("prefix")) > 0 {
|
||||
vals["prefix"] = c.String("prefix")
|
||||
}
|
||||
if len(c.String("flags")) > 0 {
|
||||
vals["flags"] = log.FlagsFromString(c.String("flags"))
|
||||
}
|
||||
if c.IsSet("color") {
|
||||
vals["colorize"] = c.Bool("color")
|
||||
}
|
||||
group := "default"
|
||||
if c.IsSet("group") {
|
||||
group = c.String("group")
|
||||
}
|
||||
name := mode
|
||||
if c.IsSet("name") {
|
||||
name = c.String("name")
|
||||
}
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
statusCode, msg := private.AddLogger(ctx, group, name, mode, vals)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runPauseLogging(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.PauseLogging(ctx)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runResumeLogging(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.ResumeLogging(ctx)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runReleaseReopenLogging(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.ReleaseReopenLogging(ctx)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runSetLogSQL(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
setup("manager", c.Bool("debug"))
|
||||
|
||||
statusCode, msg := private.SetLogSQL(ctx, !c.Bool("off"))
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/migrations"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
@ -23,10 +24,7 @@ var CmdMigrate = cli.Command{
|
|||
}
|
||||
|
||||
func runMigrate(ctx *cli.Context) error {
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(stdCtx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -34,9 +32,9 @@ func runMigrate(ctx *cli.Context) error {
|
|||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Info("Custom path: %s", setting.CustomPath)
|
||||
log.Info("Log path: %s", setting.LogRootPath)
|
||||
log.Info("Configuration file: %s", setting.CustomConf)
|
||||
setting.InitDBConfig()
|
||||
|
||||
if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
|
||||
if err := models.NewEngine(context.Background(), migrations.Migrate); err != nil {
|
||||
log.Fatal("Failed to initialize ORM engine: %v", err)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
|
@ -8,14 +9,9 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/migrations"
|
||||
packages_model "code.gitea.io/gitea/models/packages"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
packages_module "code.gitea.io/gitea/modules/packages"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
|
||||
|
@ -26,13 +22,13 @@ import (
|
|||
var CmdMigrateStorage = cli.Command{
|
||||
Name: "migrate-storage",
|
||||
Usage: "Migrate the storage",
|
||||
Description: "Copies stored files from storage configured in app.ini to parameter-configured storage",
|
||||
Description: "This is a command for migrating storage.",
|
||||
Action: runMigrateStorage,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "type, t",
|
||||
Value: "",
|
||||
Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages'",
|
||||
Usage: "Kinds of files to migrate, currently only 'attachments' is supported",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "storage, s",
|
||||
|
@ -81,55 +77,36 @@ var CmdMigrateStorage = cli.Command{
|
|||
},
|
||||
}
|
||||
|
||||
func migrateAttachments(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||
return db.Iterate(ctx, nil, func(ctx context.Context, attach *repo_model.Attachment) error {
|
||||
func migrateAttachments(dstStorage storage.ObjectStorage) error {
|
||||
return models.IterateAttachment(func(attach *models.Attachment) error {
|
||||
_, err := storage.Copy(dstStorage, attach.RelativePath(), storage.Attachments, attach.RelativePath())
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func migrateLFS(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||
return db.Iterate(ctx, nil, func(ctx context.Context, mo *git_model.LFSMetaObject) error {
|
||||
func migrateLFS(dstStorage storage.ObjectStorage) error {
|
||||
return models.IterateLFS(func(mo *models.LFSMetaObject) error {
|
||||
_, err := storage.Copy(dstStorage, mo.RelativePath(), storage.LFS, mo.RelativePath())
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func migrateAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||
return db.Iterate(ctx, nil, func(ctx context.Context, user *user_model.User) error {
|
||||
func migrateAvatars(dstStorage storage.ObjectStorage) error {
|
||||
return models.IterateUser(func(user *models.User) error {
|
||||
_, err := storage.Copy(dstStorage, user.CustomAvatarRelativePath(), storage.Avatars, user.CustomAvatarRelativePath())
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func migrateRepoAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||
return db.Iterate(ctx, nil, func(ctx context.Context, repo *repo_model.Repository) error {
|
||||
func migrateRepoAvatars(dstStorage storage.ObjectStorage) error {
|
||||
return models.IterateRepository(func(repo *models.Repository) error {
|
||||
_, err := storage.Copy(dstStorage, repo.CustomAvatarRelativePath(), storage.RepoAvatars, repo.CustomAvatarRelativePath())
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func migrateRepoArchivers(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||
return db.Iterate(ctx, nil, func(ctx context.Context, archiver *repo_model.RepoArchiver) error {
|
||||
p := archiver.RelativePath()
|
||||
_, err := storage.Copy(dstStorage, p, storage.RepoArchives, p)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func migratePackages(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||
return db.Iterate(ctx, nil, func(ctx context.Context, pb *packages_model.PackageBlob) error {
|
||||
p := packages_module.KeyToRelativePath(packages_module.BlobHash256Key(pb.HashSHA256))
|
||||
_, err := storage.Copy(dstStorage, p, storage.Packages, p)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func runMigrateStorage(ctx *cli.Context) error {
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(stdCtx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -137,13 +114,15 @@ func runMigrateStorage(ctx *cli.Context) error {
|
|||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Info("Custom path: %s", setting.CustomPath)
|
||||
log.Info("Log path: %s", setting.LogRootPath)
|
||||
log.Info("Configuration file: %s", setting.CustomConf)
|
||||
setting.InitDBConfig()
|
||||
|
||||
if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
|
||||
if err := models.NewEngine(context.Background(), migrations.Migrate); err != nil {
|
||||
log.Fatal("Failed to initialize ORM engine: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
goCtx := context.Background()
|
||||
|
||||
if err := storage.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -160,13 +139,13 @@ func runMigrateStorage(ctx *cli.Context) error {
|
|||
return nil
|
||||
}
|
||||
dstStorage, err = storage.NewLocalStorage(
|
||||
stdCtx,
|
||||
goCtx,
|
||||
storage.LocalStorageConfig{
|
||||
Path: p,
|
||||
})
|
||||
case string(storage.MinioStorageType):
|
||||
dstStorage, err = storage.NewMinioStorage(
|
||||
stdCtx,
|
||||
goCtx,
|
||||
storage.MinioStorageConfig{
|
||||
Endpoint: ctx.String("minio-endpoint"),
|
||||
AccessKeyID: ctx.String("minio-access-key-id"),
|
||||
|
@ -177,29 +156,35 @@ func runMigrateStorage(ctx *cli.Context) error {
|
|||
UseSSL: ctx.Bool("minio-use-ssl"),
|
||||
})
|
||||
default:
|
||||
return fmt.Errorf("unsupported storage type: %s", ctx.String("storage"))
|
||||
return fmt.Errorf("Unsupported storage type: %s", ctx.String("storage"))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
migratedMethods := map[string]func(context.Context, storage.ObjectStorage) error{
|
||||
"attachments": migrateAttachments,
|
||||
"lfs": migrateLFS,
|
||||
"avatars": migrateAvatars,
|
||||
"repo-avatars": migrateRepoAvatars,
|
||||
"repo-archivers": migrateRepoArchivers,
|
||||
"packages": migratePackages,
|
||||
}
|
||||
|
||||
tp := strings.ToLower(ctx.String("type"))
|
||||
if m, ok := migratedMethods[tp]; ok {
|
||||
if err := m(stdCtx, dstStorage); err != nil {
|
||||
switch tp {
|
||||
case "attachments":
|
||||
if err := migrateAttachments(dstStorage); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("%s files have successfully been copied to the new storage.", tp)
|
||||
return nil
|
||||
case "lfs":
|
||||
if err := migrateLFS(dstStorage); err != nil {
|
||||
return err
|
||||
}
|
||||
case "avatars":
|
||||
if err := migrateAvatars(dstStorage); err != nil {
|
||||
return err
|
||||
}
|
||||
case "repo-avatars":
|
||||
if err := migrateRepoAvatars(dstStorage); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("Unsupported storage: %s", ctx.String("type"))
|
||||
}
|
||||
|
||||
return fmt.Errorf("unsupported storage: %s", ctx.String("type"))
|
||||
log.Warn("All files have been copied to the new placement but old files are still on the original placement.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/packages"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
packages_module "code.gitea.io/gitea/modules/packages"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
packages_service "code.gitea.io/gitea/services/packages"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMigratePackages(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
creator := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
|
||||
content := "package main\n\nfunc main() {\nfmt.Println(\"hi\")\n}\n"
|
||||
buf, err := packages_module.CreateHashedBufferFromReader(strings.NewReader(content), 1024)
|
||||
assert.NoError(t, err)
|
||||
defer buf.Close()
|
||||
|
||||
v, f, err := packages_service.CreatePackageAndAddFile(&packages_service.PackageCreationInfo{
|
||||
PackageInfo: packages_service.PackageInfo{
|
||||
Owner: creator,
|
||||
PackageType: packages.TypeGeneric,
|
||||
Name: "test",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
Creator: creator,
|
||||
SemverCompatible: true,
|
||||
VersionProperties: map[string]string{},
|
||||
}, &packages_service.PackageFileCreationInfo{
|
||||
PackageFileInfo: packages_service.PackageFileInfo{
|
||||
Filename: "a.go",
|
||||
},
|
||||
Creator: creator,
|
||||
Data: buf,
|
||||
IsLead: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, v)
|
||||
assert.NotNil(t, f)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
p := t.TempDir()
|
||||
|
||||
dstStorage, err := storage.NewLocalStorage(
|
||||
ctx,
|
||||
storage.LocalStorageConfig{
|
||||
Path: p,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = migratePackages(ctx, dstStorage)
|
||||
assert.NoError(t, err)
|
||||
|
||||
entries, err := os.ReadDir(p)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 2, len(entries))
|
||||
assert.EqualValues(t, "01", entries[0].Name())
|
||||
assert.EqualValues(t, "tmp", entries[1].Name())
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
|
@ -43,10 +43,6 @@ var CmdRestoreRepository = cli.Command{
|
|||
Usage: `Which items will be restored, one or more units should be separated as comma.
|
||||
wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "validation",
|
||||
Usage: "Sanity check the content of the files before trying to load them",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -54,18 +50,14 @@ func runRestoreRepository(c *cli.Context) error {
|
|||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setting.LoadFromExisting()
|
||||
var units []string
|
||||
if s := c.String("units"); s != "" {
|
||||
units = strings.Split(s, ",")
|
||||
}
|
||||
setting.NewContext()
|
||||
|
||||
statusCode, errStr := private.RestoreRepo(
|
||||
ctx,
|
||||
c.String("repo_dir"),
|
||||
c.String("owner_name"),
|
||||
c.String("repo_name"),
|
||||
units,
|
||||
c.Bool("validation"),
|
||||
c.StringSlice("units"),
|
||||
)
|
||||
if statusCode == http.StatusOK {
|
||||
return nil
|
||||
|
|
112
cmd/serv.go
112
cmd/serv.go
|
@ -1,11 +1,11 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -16,20 +16,15 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/pprof"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/services/lfs"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/golang-jwt/jwt"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/kballard/go-shellquote"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
@ -42,7 +37,7 @@ const (
|
|||
var CmdServ = cli.Command{
|
||||
Name: "serv",
|
||||
Usage: "This command should only be called by SSH shell",
|
||||
Description: "Serv provides access auth for repositories",
|
||||
Description: `Serv provide access auth for repositories`,
|
||||
Action: runServ,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
|
@ -61,33 +56,18 @@ func setup(logPath string, debug bool) {
|
|||
} else {
|
||||
_ = log.NewLogger(1000, "console", "console", `{"level":"fatal","stacktracelevel":"NONE","stderr":true}`)
|
||||
}
|
||||
setting.LoadFromExisting()
|
||||
setting.NewContext()
|
||||
if debug {
|
||||
setting.RunMode = "dev"
|
||||
}
|
||||
|
||||
// Check if setting.RepoRootPath exists. It could be the case that it doesn't exist, this can happen when
|
||||
// `[repository]` `ROOT` is a relative path and $GITEA_WORK_DIR isn't passed to the SSH connection.
|
||||
if _, err := os.Stat(setting.RepoRootPath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
_ = fail("Incorrect configuration, no repository directory.", "Directory `[repository].ROOT` %q was not found, please check if $GITEA_WORK_DIR is passed to the SSH connection or make `[repository].ROOT` an absolute value.", setting.RepoRootPath)
|
||||
} else {
|
||||
_ = fail("Incorrect configuration, repository directory is inaccessible", "Directory `[repository].ROOT` %q is inaccessible. err: %v", setting.RepoRootPath, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := git.InitSimple(context.Background()); err != nil {
|
||||
_ = fail("Failed to init git", "Failed to init git, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
allowedCommands = map[string]perm.AccessMode{
|
||||
"git-upload-pack": perm.AccessModeRead,
|
||||
"git-upload-archive": perm.AccessModeRead,
|
||||
"git-receive-pack": perm.AccessModeWrite,
|
||||
lfsAuthenticateVerb: perm.AccessModeNone,
|
||||
allowedCommands = map[string]models.AccessMode{
|
||||
"git-upload-pack": models.AccessModeRead,
|
||||
"git-upload-archive": models.AccessModeRead,
|
||||
"git-receive-pack": models.AccessModeWrite,
|
||||
lfsAuthenticateVerb: models.AccessModeNone,
|
||||
}
|
||||
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||
)
|
||||
|
@ -95,12 +75,12 @@ var (
|
|||
func fail(userMessage, logMessage string, args ...interface{}) error {
|
||||
// There appears to be a chance to cause a zombie process and failure to read the Exit status
|
||||
// if nothing is outputted on stdout.
|
||||
_, _ = fmt.Fprintln(os.Stdout, "")
|
||||
_, _ = fmt.Fprintln(os.Stderr, "Gitea:", userMessage)
|
||||
fmt.Fprintln(os.Stdout, "")
|
||||
fmt.Fprintln(os.Stderr, "Gitea:", userMessage)
|
||||
|
||||
if len(logMessage) > 0 {
|
||||
if !setting.IsProd {
|
||||
_, _ = fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
|
||||
if !setting.IsProd() {
|
||||
fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
|
||||
}
|
||||
}
|
||||
ctx, cancel := installSignals()
|
||||
|
@ -109,7 +89,7 @@ func fail(userMessage, logMessage string, args ...interface{}) error {
|
|||
if len(logMessage) > 0 {
|
||||
_ = private.SSHLog(ctx, true, fmt.Sprintf(logMessage+": ", args...))
|
||||
}
|
||||
return cli.NewExitError("", 1)
|
||||
return cli.NewExitError(fmt.Sprintf("Gitea: %s", userMessage), 1)
|
||||
}
|
||||
|
||||
func runServ(c *cli.Context) error {
|
||||
|
@ -147,9 +127,9 @@ func runServ(c *cli.Context) error {
|
|||
return fail("Internal error", "Failed to check provided key: %v", err)
|
||||
}
|
||||
switch key.Type {
|
||||
case asymkey_model.KeyTypeDeploy:
|
||||
case models.KeyTypeDeploy:
|
||||
println("Hi there! You've successfully authenticated with the deploy key named " + key.Name + ", but Gitea does not provide shell access.")
|
||||
case asymkey_model.KeyTypePrincipal:
|
||||
case models.KeyTypePrincipal:
|
||||
println("Hi there! You've successfully authenticated with the principal " + key.Content + ", but Gitea does not provide shell access.")
|
||||
default:
|
||||
println("Hi there, " + user.Name + "! You've successfully authenticated with the key named " + key.Name + ", but Gitea does not provide shell access.")
|
||||
|
@ -166,13 +146,6 @@ func runServ(c *cli.Context) error {
|
|||
}
|
||||
|
||||
if len(words) < 2 {
|
||||
if git.CheckGitVersionAtLeast("2.29") == nil {
|
||||
// for AGit Flow
|
||||
if cmd == "ssh_info" {
|
||||
fmt.Print(`{"type":"gitea","version":1}`)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fail("Too few arguments", "Too few arguments in cmd: %s", cmd)
|
||||
}
|
||||
|
||||
|
@ -208,7 +181,7 @@ func runServ(c *cli.Context) error {
|
|||
return fail("Invalid repo name", "Invalid repo name: %s", reponame)
|
||||
}
|
||||
|
||||
if c.Bool("enable-pprof") {
|
||||
if setting.EnablePprof || c.Bool("enable-pprof") {
|
||||
if err := os.MkdirAll(setting.PprofDataPath, os.ModePerm); err != nil {
|
||||
return fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err)
|
||||
}
|
||||
|
@ -233,9 +206,9 @@ func runServ(c *cli.Context) error {
|
|||
|
||||
if verb == lfsAuthenticateVerb {
|
||||
if lfsVerb == "upload" {
|
||||
requestedMode = perm.AccessModeWrite
|
||||
requestedMode = models.AccessModeWrite
|
||||
} else if lfsVerb == "download" {
|
||||
requestedMode = perm.AccessModeRead
|
||||
requestedMode = models.AccessModeRead
|
||||
} else {
|
||||
return fail("Unknown LFS verb", "Unknown lfs verb %s", lfsVerb)
|
||||
}
|
||||
|
@ -252,16 +225,27 @@ func runServ(c *cli.Context) error {
|
|||
}
|
||||
return fail("Internal Server Error", "%s", err.Error())
|
||||
}
|
||||
os.Setenv(models.EnvRepoIsWiki, strconv.FormatBool(results.IsWiki))
|
||||
os.Setenv(models.EnvRepoName, results.RepoName)
|
||||
os.Setenv(models.EnvRepoUsername, results.OwnerName)
|
||||
os.Setenv(models.EnvPusherName, results.UserName)
|
||||
os.Setenv(models.EnvPusherEmail, results.UserEmail)
|
||||
os.Setenv(models.EnvPusherID, strconv.FormatInt(results.UserID, 10))
|
||||
os.Setenv(models.EnvRepoID, strconv.FormatInt(results.RepoID, 10))
|
||||
os.Setenv(models.EnvPRID, fmt.Sprintf("%d", 0))
|
||||
os.Setenv(models.EnvIsDeployKey, fmt.Sprintf("%t", results.IsDeployKey))
|
||||
os.Setenv(models.EnvKeyID, fmt.Sprintf("%d", results.KeyID))
|
||||
os.Setenv(models.EnvAppURL, setting.AppURL)
|
||||
|
||||
// LFS token authentication
|
||||
//LFS token authentication
|
||||
if verb == lfsAuthenticateVerb {
|
||||
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
|
||||
|
||||
now := time.Now()
|
||||
claims := lfs.Claims{
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(now.Add(setting.LFS.HTTPAuthExpiry)),
|
||||
NotBefore: jwt.NewNumericDate(now),
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
ExpiresAt: now.Add(setting.LFS.HTTPAuthExpiry).Unix(),
|
||||
NotBefore: now.Unix(),
|
||||
},
|
||||
RepoID: results.RepoID,
|
||||
Op: lfsVerb,
|
||||
|
@ -275,12 +259,13 @@ func runServ(c *cli.Context) error {
|
|||
return fail("Internal error", "Failed to sign JWT token: %v", err)
|
||||
}
|
||||
|
||||
tokenAuthentication := &git_model.LFSTokenResponse{
|
||||
tokenAuthentication := &models.LFSTokenResponse{
|
||||
Header: make(map[string]string),
|
||||
Href: url,
|
||||
}
|
||||
tokenAuthentication.Header["Authorization"] = fmt.Sprintf("Bearer %s", tokenString)
|
||||
|
||||
json := jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
err = enc.Encode(tokenAuthentication)
|
||||
if err != nil {
|
||||
|
@ -302,29 +287,10 @@ func runServ(c *cli.Context) error {
|
|||
gitcmd = exec.CommandContext(ctx, verb, repoPath)
|
||||
}
|
||||
|
||||
process.SetSysProcAttribute(gitcmd)
|
||||
gitcmd.Dir = setting.RepoRootPath
|
||||
gitcmd.Stdout = os.Stdout
|
||||
gitcmd.Stdin = os.Stdin
|
||||
gitcmd.Stderr = os.Stderr
|
||||
gitcmd.Env = append(gitcmd.Env, os.Environ()...)
|
||||
gitcmd.Env = append(gitcmd.Env,
|
||||
repo_module.EnvRepoIsWiki+"="+strconv.FormatBool(results.IsWiki),
|
||||
repo_module.EnvRepoName+"="+results.RepoName,
|
||||
repo_module.EnvRepoUsername+"="+results.OwnerName,
|
||||
repo_module.EnvPusherName+"="+results.UserName,
|
||||
repo_module.EnvPusherEmail+"="+results.UserEmail,
|
||||
repo_module.EnvPusherID+"="+strconv.FormatInt(results.UserID, 10),
|
||||
repo_module.EnvRepoID+"="+strconv.FormatInt(results.RepoID, 10),
|
||||
repo_module.EnvPRID+"="+fmt.Sprintf("%d", 0),
|
||||
repo_module.EnvDeployKeyID+"="+fmt.Sprintf("%d", results.DeployKeyID),
|
||||
repo_module.EnvKeyID+"="+fmt.Sprintf("%d", results.KeyID),
|
||||
repo_module.EnvAppURL+"="+setting.AppURL,
|
||||
)
|
||||
// to avoid breaking, here only use the minimal environment variables for the "gitea serv" command.
|
||||
// it could be re-considered whether to use the same git.CommonGitCmdEnvs() as "git" command later.
|
||||
gitcmd.Env = append(gitcmd.Env, git.CommonCmdServEnvs()...)
|
||||
|
||||
if err = gitcmd.Run(); err != nil {
|
||||
return fail("Internal error", "Failed to execute git command: %v", err)
|
||||
}
|
||||
|
|
68
cmd/web.go
68
cmd/web.go
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
|
@ -8,19 +9,17 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
|
||||
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/routers"
|
||||
"code.gitea.io/gitea/routers/install"
|
||||
|
||||
"github.com/felixge/fgprof"
|
||||
context2 "github.com/gorilla/context"
|
||||
"github.com/urfave/cli"
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
@ -60,9 +59,6 @@ and it takes care of all the other things for you`,
|
|||
}
|
||||
|
||||
func runHTTPRedirector() {
|
||||
_, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: HTTP Redirector", process.SystemProcessType, true)
|
||||
defer finished()
|
||||
|
||||
source := fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.PortToRedirect)
|
||||
dest := strings.TrimSuffix(setting.AppURL, "/")
|
||||
log.Info("Redirecting: %s to %s", source, dest)
|
||||
|
@ -75,7 +71,8 @@ func runHTTPRedirector() {
|
|||
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
||||
})
|
||||
|
||||
err := runHTTP("tcp", source, "HTTP Redirector", handler, setting.RedirectorUseProxyProtocol)
|
||||
var err = runHTTP("tcp", source, "HTTP Redirector", context2.ClearHandler(handler))
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("Failed to start port redirection: %v", err)
|
||||
}
|
||||
|
@ -91,7 +88,7 @@ func runWeb(ctx *cli.Context) error {
|
|||
}
|
||||
defer func() {
|
||||
if panicked := recover(); panicked != nil {
|
||||
log.Fatal("PANIC: %v\n%s", panicked, log.Stack(2))
|
||||
log.Fatal("PANIC: %v\n%s", panicked, string(log.Stack(2)))
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -125,14 +122,8 @@ func runWeb(ctx *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
installCtx, cancel := context.WithCancel(graceful.GetManager().HammerContext())
|
||||
c := install.Routes(installCtx)
|
||||
c := install.Routes()
|
||||
err := listen(c, false)
|
||||
cancel()
|
||||
if err != nil {
|
||||
log.Critical("Unable to open listener for installer. Is Gitea already running?")
|
||||
graceful.GetManager().DoGracefulShutdown()
|
||||
}
|
||||
select {
|
||||
case <-graceful.GetManager().IsShutdown():
|
||||
<-graceful.GetManager().Done()
|
||||
|
@ -147,26 +138,14 @@ func runWeb(ctx *cli.Context) error {
|
|||
|
||||
if setting.EnablePprof {
|
||||
go func() {
|
||||
http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler())
|
||||
_, _, finished := process.GetManager().AddTypedContext(context.Background(), "Web: PProf Server", process.SystemProcessType, true)
|
||||
// The pprof server is for debug purpose only, it shouldn't be exposed on public network. At the moment it's not worth to introduce a configurable option for it.
|
||||
log.Info("Starting pprof server on localhost:6060")
|
||||
log.Info("Stopped pprof server: %v", http.ListenAndServe("localhost:6060", nil))
|
||||
finished()
|
||||
log.Info("%v", http.ListenAndServe("localhost:6060", nil))
|
||||
}()
|
||||
}
|
||||
|
||||
log.Info("Global init")
|
||||
// Perform global initialization
|
||||
setting.LoadFromExisting()
|
||||
routers.GlobalInitInstalled(graceful.GetManager().HammerContext())
|
||||
|
||||
// We check that AppDataPath exists here (it should have been created during installation)
|
||||
// We can't check it in `GlobalInitInstalled`, because some integration tests
|
||||
// use cmd -> GlobalInitInstalled, but the AppDataPath doesn't exist during those tests.
|
||||
if _, err := os.Stat(setting.AppDataPath); err != nil {
|
||||
log.Fatal("Can not find APP_DATA_PATH '%s'", setting.AppDataPath)
|
||||
}
|
||||
routers.GlobalInit(graceful.GetManager().HammerContext())
|
||||
|
||||
// Override the provided port number within the configuration
|
||||
if ctx.IsSet("port") {
|
||||
|
@ -176,7 +155,7 @@ func runWeb(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
// Set up Chi routes
|
||||
c := routers.NormalRoutes(graceful.GetManager().HammerContext())
|
||||
c := routers.NormalRoutes()
|
||||
err := listen(c, true)
|
||||
<-graceful.GetManager().Done()
|
||||
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
||||
|
@ -189,7 +168,7 @@ func setPort(port string) error {
|
|||
setting.HTTPPort = port
|
||||
|
||||
switch setting.Protocol {
|
||||
case setting.HTTPUnix:
|
||||
case setting.UnixSocket:
|
||||
case setting.FCGI:
|
||||
case setting.FCGIUnix:
|
||||
default:
|
||||
|
@ -202,7 +181,7 @@ func setPort(port string) error {
|
|||
defaultLocalURL += ":" + setting.HTTPPort + "/"
|
||||
|
||||
// Save LOCAL_ROOT_URL if port changed
|
||||
setting.CreateOrAppendToCustomConf("server.LOCAL_ROOT_URL", func(cfg *ini.File) {
|
||||
setting.CreateOrAppendToCustomConf(func(cfg *ini.File) {
|
||||
cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
|
||||
})
|
||||
}
|
||||
|
@ -211,11 +190,9 @@ func setPort(port string) error {
|
|||
|
||||
func listen(m http.Handler, handleRedirector bool) error {
|
||||
listenAddr := setting.HTTPAddr
|
||||
if setting.Protocol != setting.HTTPUnix && setting.Protocol != setting.FCGIUnix {
|
||||
if setting.Protocol != setting.UnixSocket && setting.Protocol != setting.FCGIUnix {
|
||||
listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort)
|
||||
}
|
||||
_, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: Gitea Server", process.SystemProcessType, true)
|
||||
defer finished()
|
||||
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
|
||||
// This can be useful for users, many users do wrong to their config and get strange behaviors behind a reverse-proxy.
|
||||
// A user may fix the configuration mistake when he sees this log.
|
||||
|
@ -232,10 +209,10 @@ func listen(m http.Handler, handleRedirector bool) error {
|
|||
if handleRedirector {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runHTTP("tcp", listenAddr, "Web", m, setting.UseProxyProtocol)
|
||||
err = runHTTP("tcp", listenAddr, "Web", context2.ClearHandler(m))
|
||||
case setting.HTTPS:
|
||||
if setting.EnableAcme {
|
||||
err = runACME(listenAddr, m)
|
||||
if setting.EnableLetsEncrypt {
|
||||
err = runLetsEncrypt(listenAddr, setting.Domain, setting.LetsEncryptDirectory, setting.LetsEncryptEmail, context2.ClearHandler(m))
|
||||
break
|
||||
}
|
||||
if handleRedirector {
|
||||
|
@ -245,25 +222,26 @@ func listen(m http.Handler, handleRedirector bool) error {
|
|||
NoHTTPRedirector()
|
||||
}
|
||||
}
|
||||
err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, m, setting.UseProxyProtocol, setting.ProxyProtocolTLSBridging)
|
||||
err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
|
||||
case setting.FCGI:
|
||||
if handleRedirector {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runFCGI("tcp", listenAddr, "FCGI Web", m, setting.UseProxyProtocol)
|
||||
case setting.HTTPUnix:
|
||||
err = runFCGI("tcp", listenAddr, "FCGI Web", context2.ClearHandler(m))
|
||||
case setting.UnixSocket:
|
||||
if handleRedirector {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runHTTP("unix", listenAddr, "Web", m, setting.UseProxyProtocol)
|
||||
err = runHTTP("unix", listenAddr, "Web", context2.ClearHandler(m))
|
||||
case setting.FCGIUnix:
|
||||
if handleRedirector {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runFCGI("unix", listenAddr, "Web", m, setting.UseProxyProtocol)
|
||||
err = runFCGI("unix", listenAddr, "Web", context2.ClearHandler(m))
|
||||
default:
|
||||
log.Fatal("Invalid protocol: %s", setting.Protocol)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Critical("Failed to start server: %v", err)
|
||||
}
|
||||
|
|
135
cmd/web_acme.go
135
cmd/web_acme.go
|
@ -1,135 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
)
|
||||
|
||||
func getCARoot(path string) (*x509.CertPool, error) {
|
||||
r, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block, _ := pem.Decode(r)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("no PEM found in the file %s", path)
|
||||
}
|
||||
caRoot, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certPool := x509.NewCertPool()
|
||||
certPool.AddCert(caRoot)
|
||||
return certPool, nil
|
||||
}
|
||||
|
||||
func runACME(listenAddr string, m http.Handler) error {
|
||||
// If HTTP Challenge enabled, needs to be serving on port 80. For TLSALPN needs 443.
|
||||
// Due to docker port mapping this can't be checked programmatically
|
||||
// TODO: these are placeholders until we add options for each in settings with appropriate warning
|
||||
enableHTTPChallenge := true
|
||||
enableTLSALPNChallenge := true
|
||||
altHTTPPort := 0
|
||||
altTLSALPNPort := 0
|
||||
|
||||
if p, err := strconv.Atoi(setting.PortToRedirect); err == nil {
|
||||
altHTTPPort = p
|
||||
}
|
||||
if p, err := strconv.Atoi(setting.HTTPPort); err == nil {
|
||||
altTLSALPNPort = p
|
||||
}
|
||||
|
||||
magic := certmagic.NewDefault()
|
||||
magic.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory}
|
||||
// Try to use private CA root if provided, otherwise defaults to system's trust
|
||||
var certPool *x509.CertPool
|
||||
if setting.AcmeCARoot != "" {
|
||||
var err error
|
||||
certPool, err = getCARoot(setting.AcmeCARoot)
|
||||
if err != nil {
|
||||
log.Warn("Failed to parse CA Root certificate, using default CA trust: %v", err)
|
||||
}
|
||||
}
|
||||
myACME := certmagic.NewACMEIssuer(magic, certmagic.ACMEIssuer{
|
||||
CA: setting.AcmeURL,
|
||||
TrustedRoots: certPool,
|
||||
Email: setting.AcmeEmail,
|
||||
Agreed: setting.AcmeTOS,
|
||||
DisableHTTPChallenge: !enableHTTPChallenge,
|
||||
DisableTLSALPNChallenge: !enableTLSALPNChallenge,
|
||||
ListenHost: setting.HTTPAddr,
|
||||
AltTLSALPNPort: altTLSALPNPort,
|
||||
AltHTTPPort: altHTTPPort,
|
||||
})
|
||||
|
||||
magic.Issuers = []certmagic.Issuer{myACME}
|
||||
|
||||
// this obtains certificates or renews them if necessary
|
||||
err := magic.ManageSync(graceful.GetManager().HammerContext(), []string{setting.Domain})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tlsConfig := magic.TLSConfig()
|
||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
|
||||
|
||||
if version := toTLSVersion(setting.SSLMinimumVersion); version != 0 {
|
||||
tlsConfig.MinVersion = version
|
||||
}
|
||||
if version := toTLSVersion(setting.SSLMaximumVersion); version != 0 {
|
||||
tlsConfig.MaxVersion = version
|
||||
}
|
||||
|
||||
// Set curve preferences
|
||||
if curves := toCurvePreferences(setting.SSLCurvePreferences); len(curves) > 0 {
|
||||
tlsConfig.CurvePreferences = curves
|
||||
}
|
||||
|
||||
// Set cipher suites
|
||||
if ciphers := toTLSCiphers(setting.SSLCipherSuites); len(ciphers) > 0 {
|
||||
tlsConfig.CipherSuites = ciphers
|
||||
}
|
||||
|
||||
if enableHTTPChallenge {
|
||||
go func() {
|
||||
_, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: ACME HTTP challenge server", process.SystemProcessType, true)
|
||||
defer finished()
|
||||
|
||||
log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
|
||||
// all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
|
||||
err := runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)), setting.RedirectorUseProxyProtocol)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return runHTTPSWithTLSConfig("tcp", listenAddr, "Web", tlsConfig, m, setting.UseProxyProtocol, setting.ProxyProtocolTLSBridging)
|
||||
}
|
||||
|
||||
func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "GET" && r.Method != "HEAD" {
|
||||
http.Error(w, "Use HTTPS", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// Remove the trailing slash at the end of setting.AppURL, the request
|
||||
// URI always contains a leading slash, which would result in a double
|
||||
// slash
|
||||
target := strings.TrimSuffix(setting.AppURL, "/") + r.URL.RequestURI()
|
||||
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/fcgi"
|
||||
|
@ -14,8 +16,16 @@ import (
|
|||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
func runHTTP(network, listenAddr, name string, m http.Handler, useProxyProtocol bool) error {
|
||||
return graceful.HTTPListenAndServe(network, listenAddr, name, m, useProxyProtocol)
|
||||
func runHTTP(network, listenAddr, name string, m http.Handler) error {
|
||||
return graceful.HTTPListenAndServe(network, listenAddr, name, m)
|
||||
}
|
||||
|
||||
func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler) error {
|
||||
return graceful.HTTPListenAndServeTLS(network, listenAddr, name, certFile, keyFile, m)
|
||||
}
|
||||
|
||||
func runHTTPSWithTLSConfig(network, listenAddr, name string, tlsConfig *tls.Config, m http.Handler) error {
|
||||
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m)
|
||||
}
|
||||
|
||||
// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
|
||||
|
@ -35,7 +45,7 @@ func NoInstallListener() {
|
|||
graceful.GetManager().InformCleanup()
|
||||
}
|
||||
|
||||
func runFCGI(network, listenAddr, name string, m http.Handler, useProxyProtocol bool) error {
|
||||
func runFCGI(network, listenAddr, name string, m http.Handler) error {
|
||||
// This needs to handle stdin as fcgi point
|
||||
fcgiServer := graceful.NewServer(network, listenAddr, name)
|
||||
|
||||
|
@ -46,7 +56,7 @@ func runFCGI(network, listenAddr, name string, m http.Handler, useProxyProtocol
|
|||
}
|
||||
m.ServeHTTP(resp, req)
|
||||
}))
|
||||
}, useProxyProtocol)
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal("Failed to start FCGI main server: %v", err)
|
||||
}
|
||||
|
|
191
cmd/web_https.go
191
cmd/web_https.go
|
@ -1,191 +0,0 @@
|
|||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/klauspost/cpuid/v2"
|
||||
)
|
||||
|
||||
var tlsVersionStringMap = map[string]uint16{
|
||||
"": tls.VersionTLS12, // Default to tls.VersionTLS12
|
||||
"tlsv1.0": tls.VersionTLS10,
|
||||
"tlsv1.1": tls.VersionTLS11,
|
||||
"tlsv1.2": tls.VersionTLS12,
|
||||
"tlsv1.3": tls.VersionTLS13,
|
||||
}
|
||||
|
||||
func toTLSVersion(version string) uint16 {
|
||||
tlsVersion, ok := tlsVersionStringMap[strings.TrimSpace(strings.ToLower(version))]
|
||||
if !ok {
|
||||
log.Warn("Unknown tls version: %s", version)
|
||||
return 0
|
||||
}
|
||||
return tlsVersion
|
||||
}
|
||||
|
||||
var curveStringMap = map[string]tls.CurveID{
|
||||
"x25519": tls.X25519,
|
||||
"p256": tls.CurveP256,
|
||||
"p384": tls.CurveP384,
|
||||
"p521": tls.CurveP521,
|
||||
}
|
||||
|
||||
func toCurvePreferences(preferences []string) []tls.CurveID {
|
||||
ids := make([]tls.CurveID, 0, len(preferences))
|
||||
for _, pref := range preferences {
|
||||
id, ok := curveStringMap[strings.TrimSpace(strings.ToLower(pref))]
|
||||
if !ok {
|
||||
log.Warn("Unknown curve: %s", pref)
|
||||
}
|
||||
if id != 0 {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
var cipherStringMap = map[string]uint16{
|
||||
"rsa_with_rc4_128_sha": tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||
"rsa_with_3des_ede_cbc_sha": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
"rsa_with_aes_128_cbc_sha": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
"rsa_with_aes_256_cbc_sha": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
"rsa_with_aes_128_cbc_sha256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
"rsa_with_aes_128_gcm_sha256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"rsa_with_aes_256_gcm_sha384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"ecdhe_ecdsa_with_rc4_128_sha": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
"ecdhe_ecdsa_with_aes_128_cbc_sha": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
"ecdhe_ecdsa_with_aes_256_cbc_sha": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
"ecdhe_rsa_with_rc4_128_sha": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
"ecdhe_rsa_with_3des_ede_cbc_sha": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
"ecdhe_rsa_with_aes_128_cbc_sha": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
"ecdhe_rsa_with_aes_256_cbc_sha": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
"ecdhe_ecdsa_with_aes_128_cbc_sha256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
"ecdhe_rsa_with_aes_128_cbc_sha256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
"ecdhe_rsa_with_aes_128_gcm_sha256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"ecdhe_ecdsa_with_aes_128_gcm_sha256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
"ecdhe_rsa_with_aes_256_gcm_sha384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"ecdhe_ecdsa_with_aes_256_gcm_sha384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
"ecdhe_rsa_with_chacha20_poly1305_sha256": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
"ecdhe_ecdsa_with_chacha20_poly1305_sha256": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
"ecdhe_rsa_with_chacha20_poly1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
"ecdhe_ecdsa_with_chacha20_poly1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
"aes_128_gcm_sha256": tls.TLS_AES_128_GCM_SHA256,
|
||||
"aes_256_gcm_sha384": tls.TLS_AES_256_GCM_SHA384,
|
||||
"chacha20_poly1305_sha256": tls.TLS_CHACHA20_POLY1305_SHA256,
|
||||
}
|
||||
|
||||
func toTLSCiphers(cipherStrings []string) []uint16 {
|
||||
ciphers := make([]uint16, 0, len(cipherStrings))
|
||||
for _, cipherString := range cipherStrings {
|
||||
cipher, ok := cipherStringMap[strings.TrimSpace(strings.ToLower(cipherString))]
|
||||
if !ok {
|
||||
log.Warn("Unknown cipher: %s", cipherString)
|
||||
}
|
||||
if cipher != 0 {
|
||||
ciphers = append(ciphers, cipher)
|
||||
}
|
||||
}
|
||||
|
||||
return ciphers
|
||||
}
|
||||
|
||||
// defaultCiphers uses hardware support to check if AES is specifically
|
||||
// supported by the CPU.
|
||||
//
|
||||
// If AES is supported AES ciphers will be preferred over ChaCha based ciphers
|
||||
// (This code is directly inspired by the certmagic code.)
|
||||
func defaultCiphers() []uint16 {
|
||||
if cpuid.CPU.Supports(cpuid.AESNI) {
|
||||
return defaultCiphersAESfirst
|
||||
}
|
||||
return defaultCiphersChaChaFirst
|
||||
}
|
||||
|
||||
var (
|
||||
defaultCiphersAES = []uint16{
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
}
|
||||
|
||||
defaultCiphersChaCha = []uint16{
|
||||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
}
|
||||
|
||||
defaultCiphersAESfirst = append(defaultCiphersAES, defaultCiphersChaCha...)
|
||||
defaultCiphersChaChaFirst = append(defaultCiphersChaCha, defaultCiphersAES...)
|
||||
)
|
||||
|
||||
// runHTTPS listens on the provided network address and then calls
|
||||
// Serve to handle requests on incoming TLS connections.
|
||||
//
|
||||
// Filenames containing a certificate and matching private key for the server must
|
||||
// be provided. If the certificate is signed by a certificate authority, the
|
||||
// certFile should be the concatenation of the server's certificate followed by the
|
||||
// CA's certificate.
|
||||
func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler, useProxyProtocol, proxyProtocolTLSBridging bool) error {
|
||||
tlsConfig := &tls.Config{}
|
||||
if tlsConfig.NextProtos == nil {
|
||||
tlsConfig.NextProtos = []string{"h2", "http/1.1"}
|
||||
}
|
||||
|
||||
if version := toTLSVersion(setting.SSLMinimumVersion); version != 0 {
|
||||
tlsConfig.MinVersion = version
|
||||
}
|
||||
if version := toTLSVersion(setting.SSLMaximumVersion); version != 0 {
|
||||
tlsConfig.MaxVersion = version
|
||||
}
|
||||
|
||||
// Set curve preferences
|
||||
tlsConfig.CurvePreferences = []tls.CurveID{
|
||||
tls.X25519,
|
||||
tls.CurveP256,
|
||||
}
|
||||
if curves := toCurvePreferences(setting.SSLCurvePreferences); len(curves) > 0 {
|
||||
tlsConfig.CurvePreferences = curves
|
||||
}
|
||||
|
||||
// Set cipher suites
|
||||
tlsConfig.CipherSuites = defaultCiphers()
|
||||
if ciphers := toTLSCiphers(setting.SSLCipherSuites); len(ciphers) > 0 {
|
||||
tlsConfig.CipherSuites = ciphers
|
||||
}
|
||||
|
||||
tlsConfig.Certificates = make([]tls.Certificate, 1)
|
||||
|
||||
certPEMBlock, err := os.ReadFile(certFile)
|
||||
if err != nil {
|
||||
log.Error("Failed to load https cert file %s for %s:%s: %v", certFile, network, listenAddr, err)
|
||||
return err
|
||||
}
|
||||
|
||||
keyPEMBlock, err := os.ReadFile(keyFile)
|
||||
if err != nil {
|
||||
log.Error("Failed to load https key file %s for %s:%s: %v", keyFile, network, listenAddr, err)
|
||||
return err
|
||||
}
|
||||
|
||||
tlsConfig.Certificates[0], err = tls.X509KeyPair(certPEMBlock, keyPEMBlock)
|
||||
if err != nil {
|
||||
log.Error("Failed to create certificate from cert file %s and key file %s for %s:%s: %v", certFile, keyFile, network, listenAddr, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m, useProxyProtocol, proxyProtocolTLSBridging)
|
||||
}
|
||||
|
||||
func runHTTPSWithTLSConfig(network, listenAddr, name string, tlsConfig *tls.Config, m http.Handler, useProxyProtocol, proxyProtocolTLSBridging bool) error {
|
||||
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m, useProxyProtocol, proxyProtocolTLSBridging)
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
context2 "github.com/gorilla/context"
|
||||
)
|
||||
|
||||
func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler) error {
|
||||
|
||||
// If HTTP Challenge enabled, needs to be serving on port 80. For TLSALPN needs 443.
|
||||
// Due to docker port mapping this can't be checked programmatically
|
||||
// TODO: these are placeholders until we add options for each in settings with appropriate warning
|
||||
enableHTTPChallenge := true
|
||||
enableTLSALPNChallenge := true
|
||||
altHTTPPort := 0
|
||||
altTLSALPNPort := 0
|
||||
|
||||
if p, err := strconv.Atoi(setting.PortToRedirect); err == nil {
|
||||
altHTTPPort = p
|
||||
}
|
||||
if p, err := strconv.Atoi(setting.HTTPPort); err == nil {
|
||||
altTLSALPNPort = p
|
||||
}
|
||||
|
||||
magic := certmagic.NewDefault()
|
||||
magic.Storage = &certmagic.FileStorage{Path: directory}
|
||||
myACME := certmagic.NewACMEManager(magic, certmagic.ACMEManager{
|
||||
Email: email,
|
||||
Agreed: setting.LetsEncryptTOS,
|
||||
DisableHTTPChallenge: !enableHTTPChallenge,
|
||||
DisableTLSALPNChallenge: !enableTLSALPNChallenge,
|
||||
ListenHost: setting.HTTPAddr,
|
||||
AltTLSALPNPort: altTLSALPNPort,
|
||||
AltHTTPPort: altHTTPPort,
|
||||
})
|
||||
|
||||
magic.Issuers = []certmagic.Issuer{myACME}
|
||||
|
||||
// this obtains certificates or renews them if necessary
|
||||
err := magic.ManageSync([]string{domain})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tlsConfig := magic.TLSConfig()
|
||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
|
||||
|
||||
if enableHTTPChallenge {
|
||||
go func() {
|
||||
log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
|
||||
// all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
|
||||
var err = runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)))
|
||||
if err != nil {
|
||||
log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return runHTTPSWithTLSConfig("tcp", listenAddr, "Web", tlsConfig, context2.ClearHandler(m))
|
||||
}
|
||||
|
||||
func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "GET" && r.Method != "HEAD" {
|
||||
http.Error(w, "Use HTTPS", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// Remove the trailing slash at the end of setting.AppURL, the request
|
||||
// URI always contains a leading slash, which would result in a double
|
||||
// slash
|
||||
target := strings.TrimSuffix(setting.AppURL, "/") + r.URL.RequestURI()
|
||||
http.Redirect(w, r, target, http.StatusFound)
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
|
@ -155,7 +156,6 @@ func runEnvironmentToIni(c *cli.Context) error {
|
|||
destination = setting.CustomConf
|
||||
}
|
||||
if destination != setting.CustomConf || changed {
|
||||
log.Info("Settings saved to: %q", destination)
|
||||
err = cfg.SaveTo(destination)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -224,6 +224,7 @@ func DecodeSectionKey(encoded string) (string, string) {
|
|||
if !inKey {
|
||||
if splitter := strings.Index(remaining, "__"); splitter > -1 {
|
||||
section += remaining[:splitter]
|
||||
inKey = true
|
||||
key += remaining[splitter+2:]
|
||||
} else {
|
||||
section += remaining
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
#############################################################################
|
||||
# This script sets some defaults for gitea to run in a FHS compliant manner #
|
||||
#############################################################################
|
||||
########################################################################
|
||||
# This script some defaults for gitea to run in a FHS compliant manner #
|
||||
########################################################################
|
||||
|
||||
# It assumes that you place this script as gitea in /usr/bin
|
||||
#
|
||||
|
@ -33,8 +33,10 @@ for i in "$@"; do
|
|||
done
|
||||
|
||||
if [ -z "$APP_INI_SET" ]; then
|
||||
CONF_ARG=("-c" "${GITEA_APP_INI:-$APP_INI}")
|
||||
CONF_ARG="-c \"$APP_INI\""
|
||||
fi
|
||||
|
||||
# Provide FHS compliant defaults
|
||||
GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" exec -a "$0" "$GITEA" "${CONF_ARG[@]}" "$@"
|
||||
# Provide FHS compliant defaults to
|
||||
GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" "$GITEA" $CONF_ARG "$@"
|
||||
|
||||
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
)
|
||||
|
||||
// To generate derivative fixtures, execute the following from Gitea's repository base dir:
|
||||
|
@ -30,13 +31,11 @@ var (
|
|||
func main() {
|
||||
pathToGiteaRoot := "."
|
||||
fixturesDir = filepath.Join(pathToGiteaRoot, "models", "fixtures")
|
||||
if err := unittest.CreateTestEngine(unittest.FixturesOptions{
|
||||
Dir: fixturesDir,
|
||||
}); err != nil {
|
||||
if err := models.CreateTestEngine(fixturesDir); err != nil {
|
||||
fmt.Printf("CreateTestEngine: %+v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := unittest.PrepareTestDatabase(); err != nil {
|
||||
if err := models.PrepareTestDatabase(); err != nil {
|
||||
fmt.Printf("PrepareTestDatabase: %+v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
@ -65,7 +64,7 @@ func generate(name string) error {
|
|||
return err
|
||||
}
|
||||
path := filepath.Join(fixturesDir, name+".yml")
|
||||
if err := os.WriteFile(path, []byte(data), 0o644); err != nil {
|
||||
if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil {
|
||||
return fmt.Errorf("%s: %+v", path, err)
|
||||
}
|
||||
fmt.Printf("%s created.\n", path)
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
dashboards_out
|
||||
vendor
|
|
@ -1,31 +0,0 @@
|
|||
JSONNET_FMT := jsonnetfmt -n 2 --max-blank-lines 1 --string-style s --comment-style s
|
||||
|
||||
.PHONY: all
|
||||
all: build dashboards_out
|
||||
|
||||
vendor: jsonnetfile.json
|
||||
jb install
|
||||
|
||||
.PHONY: build
|
||||
build: vendor
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \
|
||||
xargs -n 1 -- $(JSONNET_FMT) -i
|
||||
|
||||
.PHONY: lint
|
||||
lint: build
|
||||
find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \
|
||||
while read f; do \
|
||||
$(JSONNET_FMT) "$$f" | diff -u "$$f" -; \
|
||||
done
|
||||
mixtool lint mixin.libsonnet
|
||||
|
||||
dashboards_out: mixin.libsonnet config.libsonnet $(wildcard dashboards/*)
|
||||
@mkdir -p dashboards_out
|
||||
jsonnet -J vendor -m dashboards_out lib/dashboards.jsonnet
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf dashboards_out
|
|
@ -1,33 +0,0 @@
|
|||
# Gitea Mixin
|
||||
|
||||
Gitea Mixin is a set of configurable Grafana dashboards based on the metrics exported by the Gitea built-in metrics endpoint.
|
||||
|
||||
## Generate config files
|
||||
|
||||
You can manually generate dashboards, but first you should install some tools:
|
||||
|
||||
```bash
|
||||
go install github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb@latest
|
||||
go install github.com/google/go-jsonnet/cmd/jsonnet@latest
|
||||
# or in brew: brew install go-jsonnet
|
||||
```
|
||||
|
||||
For linting and formatting, you would also need `mixtool` and `jsonnetfmt` installed. If you
|
||||
have a working Go development environment, it's easiest to run the following:
|
||||
|
||||
```bash
|
||||
go install github.com/monitoring-mixins/mixtool/cmd/mixtool@latest
|
||||
go install github.com/google/go-jsonnet/cmd/jsonnetfmt@latest
|
||||
```
|
||||
|
||||
The files in `dashboards_out` need to be imported
|
||||
into your Grafana server. The exact details will be depending on your environment.
|
||||
|
||||
Edit `config.libsonnet` if required and then build JSON dashboard files for Grafana:
|
||||
|
||||
```bash
|
||||
make
|
||||
```
|
||||
|
||||
For more advanced uses of mixins, see
|
||||
https://github.com/monitoring-mixins/docs.
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue