mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-05 18:32:41 +09:00
Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12f51ec7dd | ||
|
|
82b843a5ab | ||
|
|
dcbbf37082 | ||
|
|
3e8618a543 | ||
|
|
3a2679db2e | ||
|
|
6839010bd6 | ||
|
|
80da796025 | ||
|
|
113c99512b | ||
|
|
82343f4943 | ||
|
|
d534007bc4 | ||
|
|
6466053b4d | ||
|
|
7dc8db9ea8 | ||
|
|
ecad970a26 | ||
|
|
47a5c8e1f7 | ||
|
|
6abb8d751c | ||
|
|
fdc6287973 | ||
|
|
320031fce6 | ||
|
|
ef2f18964e | ||
|
|
f2bde40804 | ||
|
|
6b1e5f7f88 | ||
|
|
56660c3fd0 | ||
|
|
87a82138c6 | ||
|
|
d06f98d9a2 | ||
|
|
c52f81eecc | ||
|
|
9749c35656 | ||
|
|
fc15e59475 | ||
|
|
78f0b5b92b | ||
|
|
20951c5c21 | ||
|
|
99f7ec8f45 | ||
|
|
a076cb2a4c | ||
|
|
530ae650f3 | ||
|
|
821570c0b0 | ||
|
|
287e2c781b | ||
|
|
921a5c0b62 | ||
|
|
0ad4083cba | ||
|
|
99058de553 | ||
|
|
fb155b8fa3 | ||
|
|
7dd9506d06 | ||
|
|
f428d40822 | ||
|
|
7339018c5e | ||
|
|
a34826b19f | ||
|
|
70739c32a9 | ||
|
|
249e22bb98 | ||
|
|
d78c31c216 | ||
|
|
795b6865af | ||
|
|
0e44fab5d6 | ||
|
|
6ad0d0a1b9 | ||
|
|
d9db28a25a | ||
|
|
5911e129a8 | ||
|
|
42f0769e30 | ||
|
|
0c40b0badd | ||
|
|
762c0463f4 | ||
|
|
bf1dbd7c56 | ||
|
|
600bb545f3 | ||
|
|
5331af1854 | ||
|
|
eb5ea5f67a | ||
|
|
5e3dd3fafe | ||
|
|
339f5bb397 | ||
|
|
1ae5435e41 | ||
|
|
ca61046f9f | ||
|
|
29368309ce | ||
|
|
a777f8ae75 | ||
|
|
80853a2238 | ||
|
|
ce958f45cd | ||
|
|
3b1e114ede | ||
|
|
573a9c6228 | ||
|
|
d131d53cbb | ||
|
|
2a6e6bf0f1 | ||
|
|
655def5141 | ||
|
|
2042cf2cce | ||
|
|
7b438b3566 | ||
|
|
8bde2e9813 | ||
|
|
8525a48581 | ||
|
|
034492384b | ||
|
|
c83bc55b52 | ||
|
|
09cc6392f6 | ||
|
|
b67eafbc21 | ||
|
|
c1ba480a7a | ||
|
|
1197512b2b | ||
|
|
1547ce5669 | ||
|
|
1aa6176bd8 | ||
|
|
2289e59bd7 | ||
|
|
6e75bc013e | ||
|
|
087719cb8d | ||
|
|
bbd9bebcc3 |
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$"]
|
|
||||||
@@ -14,28 +14,24 @@ groups:
|
|||||||
name: BREAKING
|
name: BREAKING
|
||||||
labels:
|
labels:
|
||||||
- kind/breaking
|
- kind/breaking
|
||||||
-
|
|
||||||
name: SECURITY
|
|
||||||
labels:
|
|
||||||
- kind/security
|
|
||||||
-
|
-
|
||||||
name: FEATURES
|
name: FEATURES
|
||||||
labels:
|
labels:
|
||||||
- kind/feature
|
- kind/feature
|
||||||
-
|
-
|
||||||
name: API
|
name: SECURITY
|
||||||
labels:
|
labels:
|
||||||
- kind/api
|
- kind/security
|
||||||
|
-
|
||||||
|
name: BUGFIXES
|
||||||
|
labels:
|
||||||
|
- kind/bug
|
||||||
-
|
-
|
||||||
name: ENHANCEMENTS
|
name: ENHANCEMENTS
|
||||||
labels:
|
labels:
|
||||||
- kind/enhancement
|
- kind/enhancement
|
||||||
- kind/refactor
|
- kind/refactor
|
||||||
- kind/ui
|
- kind/ui
|
||||||
-
|
|
||||||
name: BUGFIXES
|
|
||||||
labels:
|
|
||||||
- kind/bug
|
|
||||||
-
|
-
|
||||||
name: TESTING
|
name: TESTING
|
||||||
labels:
|
labels:
|
||||||
|
|||||||
822
.drone.yml
822
.drone.yml
File diff suppressed because it is too large
Load Diff
@@ -1,31 +1,25 @@
|
|||||||
root = true
|
root = true
|
||||||
|
|
||||||
[*]
|
[*]
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
tab_width = 2
|
|
||||||
end_of_line = lf
|
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
[*.{go,tmpl,html}]
|
end_of_line = lf
|
||||||
indent_style = tab
|
|
||||||
|
|
||||||
[templates/custom/*.tmpl]
|
|
||||||
insert_final_newline = false
|
|
||||||
|
|
||||||
[templates/swagger/v1_json.tmpl]
|
|
||||||
indent_style = space
|
|
||||||
|
|
||||||
[templates/user/auth/oidc_wellknown.tmpl]
|
|
||||||
indent_style = space
|
|
||||||
|
|
||||||
[Makefile]
|
|
||||||
indent_style = tab
|
|
||||||
|
|
||||||
[*.svg]
|
|
||||||
insert_final_newline = false
|
|
||||||
|
|
||||||
[*.md]
|
[*.md]
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.{go,tmpl,html}]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.{less,css}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.{js,json,yml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
|||||||
417
.eslintrc
417
.eslintrc
@@ -1,24 +1,19 @@
|
|||||||
root: true
|
root: true
|
||||||
reportUnusedDisableDirectives: true
|
|
||||||
|
extends:
|
||||||
|
- eslint-config-airbnb-base
|
||||||
|
- eslint:recommended
|
||||||
|
|
||||||
ignorePatterns:
|
ignorePatterns:
|
||||||
- /web_src/js/vendor
|
- /web_src/js/vendor
|
||||||
|
|
||||||
parserOptions:
|
parserOptions:
|
||||||
sourceType: module
|
ecmaVersion: 2020
|
||||||
ecmaVersion: latest
|
|
||||||
|
|
||||||
plugins:
|
|
||||||
- eslint-plugin-unicorn
|
|
||||||
- eslint-plugin-import
|
|
||||||
- eslint-plugin-vue
|
|
||||||
- eslint-plugin-html
|
|
||||||
|
|
||||||
extends:
|
|
||||||
- plugin:vue/recommended
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
es2021: true
|
browser: true
|
||||||
|
es6: true
|
||||||
|
jquery: true
|
||||||
node: true
|
node: true
|
||||||
|
|
||||||
globals:
|
globals:
|
||||||
@@ -26,436 +21,54 @@ globals:
|
|||||||
CodeMirror: false
|
CodeMirror: false
|
||||||
Dropzone: false
|
Dropzone: false
|
||||||
SimpleMDE: false
|
SimpleMDE: false
|
||||||
|
u2fApi: false
|
||||||
settings:
|
Tribute: false
|
||||||
html/html-extensions: [".tmpl"]
|
|
||||||
|
|
||||||
overrides:
|
overrides:
|
||||||
- files: ["web_src/**/*.js", "web_src/**/*.vue", "templates/**/*.tmpl"]
|
- files: ["web_src/**/*.worker.js", "web_src/js/serviceworker.js"]
|
||||||
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:
|
env:
|
||||||
worker: true
|
worker: true
|
||||||
rules:
|
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]
|
no-restricted-globals: [0]
|
||||||
- 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:
|
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-body-style: [0]
|
||||||
arrow-parens: [2, always]
|
arrow-parens: [2, always]
|
||||||
arrow-spacing: [2, {before: true, after: true}]
|
|
||||||
block-scoped-var: [2]
|
|
||||||
brace-style: [2, 1tbs, {allowSingleLine: true}]
|
|
||||||
camelcase: [0]
|
camelcase: [0]
|
||||||
capitalized-comments: [0]
|
|
||||||
class-methods-use-this: [0]
|
|
||||||
comma-dangle: [2, only-multiline]
|
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-return: [0]
|
||||||
consistent-this: [0]
|
|
||||||
constructor-super: [2]
|
|
||||||
curly: [0]
|
|
||||||
default-case-last: [2]
|
|
||||||
default-case: [0]
|
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-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/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: [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-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}]
|
|
||||||
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/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-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-comment-style: [2, separate-lines]
|
||||||
multiline-ternary: [0]
|
|
||||||
new-cap: [0]
|
|
||||||
new-parens: [2]
|
|
||||||
newline-per-chained-call: [0]
|
newline-per-chained-call: [0]
|
||||||
no-alert: [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-cond-assign: [2, except-parens]
|
||||||
no-confusing-arrow: [0]
|
|
||||||
no-console: [1, {allow: [info, warn, error]}]
|
no-console: [1, {allow: [info, warn, error]}]
|
||||||
no-const-assign: [2]
|
|
||||||
no-constant-condition: [0]
|
|
||||||
no-constructor-return: [2]
|
|
||||||
no-continue: [0]
|
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-empty: [2, {allowEmptyCatch: true}]
|
||||||
no-eq-null: [2]
|
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-operators: [0]
|
||||||
no-mixed-spaces-and-tabs: [2]
|
|
||||||
no-multi-assign: [0]
|
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-new: [0]
|
||||||
no-nonoctal-decimal-escape: [2]
|
|
||||||
no-obj-calls: [2]
|
|
||||||
no-octal-escape: [2]
|
|
||||||
no-octal: [2]
|
|
||||||
no-param-reassign: [0]
|
no-param-reassign: [0]
|
||||||
no-plusplus: [0]
|
no-plusplus: [0]
|
||||||
no-promise-executor-return: [0]
|
no-restricted-syntax: [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-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-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-underscore-dangle: [0]
|
||||||
no-unexpected-multiline: [2]
|
no-unused-vars: [2, {args: all, argsIgnorePattern: ^_, varsIgnorePattern: ^_, ignoreRestSiblings: true}]
|
||||||
no-unmodified-loop-condition: [2]
|
no-use-before-define: [0]
|
||||||
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: ^_, 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-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-newline: [0]
|
||||||
object-curly-spacing: [2, never]
|
object-curly-spacing: [2, never]
|
||||||
object-shorthand: [2, always]
|
|
||||||
one-var-declaration-per-line: [0]
|
one-var-declaration-per-line: [0]
|
||||||
one-var: [0]
|
one-var: [0]
|
||||||
operator-assignment: [2, always]
|
|
||||||
operator-linebreak: [2, after]
|
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-const: [2, {destructuring: all}]
|
||||||
prefer-destructuring: [0]
|
prefer-destructuring: [0]
|
||||||
prefer-exponentiation-operator: [2]
|
|
||||||
prefer-named-capture-group: [0]
|
|
||||||
prefer-numeric-literals: [2]
|
|
||||||
prefer-object-has-own: [0]
|
|
||||||
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}]
|
quotes: [2, single, {avoidEscape: true, allowTemplateLiterals: true}]
|
||||||
radix: [2, as-needed]
|
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}]
|
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: [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-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-thenable: [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-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-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: [2]
|
|
||||||
unicorn/prefer-dataset: [2]
|
|
||||||
unicorn/prefer-date-now: [2]
|
|
||||||
unicorn/prefer-default-parameters: [0]
|
|
||||||
unicorn/prefer-event-key: [2]
|
|
||||||
unicorn/prefer-export-from: [2]
|
|
||||||
unicorn/prefer-includes: [2]
|
|
||||||
unicorn/prefer-json-parse-buffer: [0]
|
|
||||||
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-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-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/template-indent: [2]
|
|
||||||
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]
|
|
||||||
|
|||||||
16
.gitattributes
vendored
16
.gitattributes
vendored
@@ -1,8 +1,10 @@
|
|||||||
* text=auto eol=lf
|
* text=auto eol=lf
|
||||||
/vendor/** -text -eol linguist-vendored
|
/vendor/** -text -eol
|
||||||
/public/vendor/** -text -eol linguist-vendored
|
/public/vendor/** -text -eol
|
||||||
/templates/**/*.tmpl linguist-language=Handlebars
|
|
||||||
/.eslintrc linguist-language=YAML
|
conf/* linguist-vendored
|
||||||
/.stylelintrc linguist-language=YAML
|
docker/* linguist-vendored
|
||||||
/web_src/fomantic/build/** linguist-generated
|
options/* linguist-vendored
|
||||||
Dockerfile.* linguist-language=Dockerfile
|
public/* linguist-vendored
|
||||||
|
build/* linguist-vendored
|
||||||
|
templates/* linguist-vendored
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
<!-- 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** -->
|
|
||||||
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@@ -1,2 +1 @@
|
|||||||
open_collective: gitea
|
open_collective: gitea
|
||||||
custom: https://www.bountysource.com/teams/gitea
|
|
||||||
|
|||||||
90
.github/ISSUE_TEMPLATE/bug-report.yaml
vendored
90
.github/ISSUE_TEMPLATE/bug-report.yaml
vendored
@@ -1,90 +0,0 @@
|
|||||||
name: Bug Report
|
|
||||||
description: Found something you weren't expecting? Report it here!
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue.
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
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. Make sure you are using the latest release and
|
|
||||||
take a moment to check that your issue hasn't been reported before.
|
|
||||||
4. Make sure it's not mentioned in the FAQ (https://docs.gitea.io/en-us/faq)
|
|
||||||
5. Please give all relevant information below for bug reports, because
|
|
||||||
incomplete details will be handled as an invalid report.
|
|
||||||
- type: input
|
|
||||||
id: gitea-ver
|
|
||||||
attributes:
|
|
||||||
label: Gitea Version
|
|
||||||
description: Gitea version (or commit reference) of your instance
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
id: git-ver
|
|
||||||
attributes:
|
|
||||||
label: Git Version
|
|
||||||
description: The version of git running on the server
|
|
||||||
- type: input
|
|
||||||
id: os-ver
|
|
||||||
attributes:
|
|
||||||
label: Operating System
|
|
||||||
description: The operating system you are using to run Gitea
|
|
||||||
- type: textarea
|
|
||||||
id: run-info
|
|
||||||
attributes:
|
|
||||||
label: How are you running Gitea?
|
|
||||||
description: |
|
|
||||||
Please include information on whether you built Gitea yourself, used one of our downloads, are using https://try.gitea.io or are using some other package
|
|
||||||
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
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: dropdown
|
|
||||||
id: database
|
|
||||||
attributes:
|
|
||||||
label: Database
|
|
||||||
description: What database system are you running?
|
|
||||||
options:
|
|
||||||
- PostgreSQL
|
|
||||||
- MySQL
|
|
||||||
- MSSQL
|
|
||||||
- SQLite
|
|
||||||
- type: dropdown
|
|
||||||
id: can-reproduce
|
|
||||||
attributes:
|
|
||||||
label: Can you reproduce the bug on the Gitea demo site?
|
|
||||||
description: |
|
|
||||||
If so, please provide a URL in the Description field
|
|
||||||
URL of Gitea demo: https://try.gitea.io
|
|
||||||
options:
|
|
||||||
- "Yes"
|
|
||||||
- "No"
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
It's really 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
|
|
||||||
- type: input
|
|
||||||
id: logs
|
|
||||||
attributes:
|
|
||||||
label: Log Gist
|
|
||||||
description: Please provide a gist URL of your logs, with any sensitive information (e.g. API keys) removed/hidden
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: |
|
|
||||||
Please provide a description of your issue here, with a URL if you were able to reproduce the issue (see above)
|
|
||||||
If you are using a proxy or a CDN (e.g. Cloudflare) in front of Gitea, please disable the proxy/CDN fully and access Gitea directly to confirm the issue still persists without those services.
|
|
||||||
- type: textarea
|
|
||||||
id: screenshots
|
|
||||||
attributes:
|
|
||||||
label: Screenshots
|
|
||||||
description: If this issue involves the Web Interface, please provide one or more screenshots
|
|
||||||
17
.github/ISSUE_TEMPLATE/config.yml
vendored
17
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,17 +0,0 @@
|
|||||||
blank_issues_enabled: true
|
|
||||||
contact_links:
|
|
||||||
- name: Security Concern
|
|
||||||
url: https://tinyurl.com/security-gitea
|
|
||||||
about: For security concerns, please send a mail to security@gitea.io instead of opening a public issue.
|
|
||||||
- name: Discord Server
|
|
||||||
url: https://discord.gg/gitea
|
|
||||||
about: Please ask questions and discuss configuration or deployment problems here.
|
|
||||||
- name: Discourse Forum
|
|
||||||
url: https://discourse.gitea.io
|
|
||||||
about: Questions and configuration or deployment problems can also be discussed on our forum.
|
|
||||||
- name: Frequently Asked Questions
|
|
||||||
url: https://docs.gitea.io/en-us/faq
|
|
||||||
about: Please check if your question isn't mentioned here.
|
|
||||||
- name: Crowdin Translations
|
|
||||||
url: https://crowdin.com/project/gitea
|
|
||||||
about: Translations are managed here.
|
|
||||||
23
.github/ISSUE_TEMPLATE/feature-request.yaml
vendored
23
.github/ISSUE_TEMPLATE/feature-request.yaml
vendored
@@ -1,23 +0,0 @@
|
|||||||
name: Feature Request
|
|
||||||
description: Got an idea for a feature that Gitea doesn't have currently? Submit your idea here!
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
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 feature hasn't already been suggested.
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Feature Description
|
|
||||||
placeholder: |
|
|
||||||
I think it would be great if Gitea had...
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: screenshots
|
|
||||||
attributes:
|
|
||||||
label: Screenshots
|
|
||||||
description: If you can, provide screenshots of an implementation on another site e.g. GitHub
|
|
||||||
62
.github/ISSUE_TEMPLATE/ui.bug-report.yaml
vendored
62
.github/ISSUE_TEMPLATE/ui.bug-report.yaml
vendored
@@ -1,62 +0,0 @@
|
|||||||
name: Web Interface Bug Report
|
|
||||||
description: Something doesn't look quite as it should? Report it here!
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue.
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
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.
|
|
||||||
- type: input
|
|
||||||
id: gitea-ver
|
|
||||||
attributes:
|
|
||||||
label: Gitea Version
|
|
||||||
description: Gitea version (or commit reference) your instance is running
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
id: os-ver
|
|
||||||
attributes:
|
|
||||||
label: Operating System
|
|
||||||
description: The operating system you are using to access Gitea
|
|
||||||
- type: input
|
|
||||||
id: browser-ver
|
|
||||||
attributes:
|
|
||||||
label: Browser Version
|
|
||||||
description: The browser and version that you are using to access Gitea
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: dropdown
|
|
||||||
id: can-reproduce
|
|
||||||
attributes:
|
|
||||||
label: Can you reproduce the bug on the Gitea demo site?
|
|
||||||
description: |
|
|
||||||
If so, please provide a URL in the Description field
|
|
||||||
URL of Gitea demo: https://try.gitea.io
|
|
||||||
options:
|
|
||||||
- "Yes"
|
|
||||||
- "No"
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: |
|
|
||||||
Please provide a description of your issue here, with a URL if you were able to reproduce the issue (see above)
|
|
||||||
If using a proxy or a CDN (e.g. CloudFlare) in front of gitea, please disable the proxy/CDN fully and connect to gitea directly to confirm the issue still persists without those services.
|
|
||||||
- type: textarea
|
|
||||||
id: screenshots
|
|
||||||
attributes:
|
|
||||||
label: Screenshots
|
|
||||||
description: Please provide at least 1 screenshot showing the issue.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
33
.github/issue_template.md
vendored
Normal file
33
.github/issue_template.md
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<!-- 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. 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:
|
||||||
|
- Database (use `[x]`):
|
||||||
|
- [ ] PostgreSQL
|
||||||
|
- [ ] MySQL
|
||||||
|
- [ ] MSSQL
|
||||||
|
- [ ] SQLite
|
||||||
|
- Can you reproduce the bug at https://try.gitea.io:
|
||||||
|
- [ ] Yes (provide example URL)
|
||||||
|
- [ ] No
|
||||||
|
- [ ] Not relevant
|
||||||
|
- Log gist:
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|
<!-- **If this issue involves the Web Interface, please include a screenshot** -->
|
||||||
23
.github/lock.yml
vendored
23
.github/lock.yml
vendored
@@ -1,23 +0,0 @@
|
|||||||
# 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
|
|
||||||
8
.github/pull_request_template.md
vendored
8
.github/pull_request_template.md
vendored
@@ -1,9 +1,7 @@
|
|||||||
<!--
|
|
||||||
|
|
||||||
Please check the following:
|
Please check the following:
|
||||||
|
|
||||||
1. Make sure you are targeting the `main` branch, pull requests on release branches are only allowed for bug fixes.
|
1. Make sure you are targeting the `master` branch, pull requests on release branches are only allowed for bug fixes.
|
||||||
2. Read contributing guidelines: https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md
|
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)
|
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.**
|
||||||
|
|||||||
27
.gitignore
vendored
27
.gitignore
vendored
@@ -9,8 +9,6 @@ _test
|
|||||||
|
|
||||||
# IntelliJ
|
# IntelliJ
|
||||||
.idea
|
.idea
|
||||||
# Goland's output filename can not be set manually
|
|
||||||
/go_build_*
|
|
||||||
|
|
||||||
# MS VSCode
|
# MS VSCode
|
||||||
.vscode
|
.vscode
|
||||||
@@ -34,7 +32,6 @@ _testmain.go
|
|||||||
|
|
||||||
*coverage.out
|
*coverage.out
|
||||||
coverage.all
|
coverage.all
|
||||||
cpu.out
|
|
||||||
|
|
||||||
/modules/options/bindata.go
|
/modules/options/bindata.go
|
||||||
/modules/options/bindata.go.hash
|
/modules/options/bindata.go.hash
|
||||||
@@ -56,7 +53,7 @@ cpu.out
|
|||||||
/custom/*
|
/custom/*
|
||||||
!/custom/conf
|
!/custom/conf
|
||||||
/custom/conf/*
|
/custom/conf/*
|
||||||
!/custom/conf/app.example.ini
|
!/custom/conf/app.ini.sample
|
||||||
/data
|
/data
|
||||||
/indexers
|
/indexers
|
||||||
/log
|
/log
|
||||||
@@ -78,28 +75,13 @@ cpu.out
|
|||||||
/integrations/mssql.ini
|
/integrations/mssql.ini
|
||||||
/node_modules
|
/node_modules
|
||||||
/yarn.lock
|
/yarn.lock
|
||||||
/yarn-error.log
|
|
||||||
/npm-debug.log*
|
|
||||||
/public/js
|
/public/js
|
||||||
/public/serviceworker.js
|
/public/serviceworker.js
|
||||||
/public/css
|
/public/css
|
||||||
/public/fonts
|
/public/fonts
|
||||||
/public/img/webpack
|
/public/fomantic
|
||||||
/vendor
|
/public/img/svg
|
||||||
/web_src/fomantic/node_modules
|
|
||||||
/web_src/fomantic/build/*
|
|
||||||
!/web_src/fomantic/build/semantic.js
|
|
||||||
!/web_src/fomantic/build/semantic.css
|
|
||||||
!/web_src/fomantic/build/themes
|
|
||||||
/web_src/fomantic/build/themes/*
|
|
||||||
!/web_src/fomantic/build/themes/default
|
|
||||||
/web_src/fomantic/build/themes/default/assets/*
|
|
||||||
!/web_src/fomantic/build/themes/default/assets/fonts
|
|
||||||
/web_src/fomantic/build/themes/default/assets/fonts/*
|
|
||||||
!/web_src/fomantic/build/themes/default/assets/fonts/icons.woff2
|
|
||||||
!/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
|
|
||||||
/VERSION
|
/VERSION
|
||||||
/.air
|
|
||||||
|
|
||||||
# Snapcraft
|
# Snapcraft
|
||||||
snap/.snapcraft/
|
snap/.snapcraft/
|
||||||
@@ -113,6 +95,3 @@ prime/
|
|||||||
|
|
||||||
# Make evidence files
|
# Make evidence files
|
||||||
/.make_evidence
|
/.make_evidence
|
||||||
|
|
||||||
# Manpage
|
|
||||||
/man
|
|
||||||
|
|||||||
@@ -9,14 +9,12 @@ linters:
|
|||||||
- unused
|
- unused
|
||||||
- structcheck
|
- structcheck
|
||||||
- varcheck
|
- varcheck
|
||||||
|
- golint
|
||||||
- dupl
|
- dupl
|
||||||
#- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
|
#- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
|
||||||
- gofmt
|
- gofmt
|
||||||
- misspell
|
- misspell
|
||||||
- gocritic
|
- gocritic
|
||||||
- bidichk
|
|
||||||
- ineffassign
|
|
||||||
- revive
|
|
||||||
enable-all: false
|
enable-all: false
|
||||||
disable-all: true
|
disable-all: true
|
||||||
fast: false
|
fast: false
|
||||||
@@ -28,35 +26,7 @@ linters-settings:
|
|||||||
gocritic:
|
gocritic:
|
||||||
disabled-checks:
|
disabled-checks:
|
||||||
- ifElseChain
|
- ifElseChain
|
||||||
- singleCaseSwitch # Every time this occurred in the code, there was no other way.
|
- singleCaseSwitch # Every time this occured 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
|
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
exclude-rules:
|
exclude-rules:
|
||||||
@@ -100,6 +70,9 @@ issues:
|
|||||||
- path: modules/log/
|
- path: modules/log/
|
||||||
linters:
|
linters:
|
||||||
- errcheck
|
- errcheck
|
||||||
|
- path: routers/routes/routes.go
|
||||||
|
linters:
|
||||||
|
- dupl
|
||||||
- path: routers/api/v1/repo/issue_subscription.go
|
- path: routers/api/v1/repo/issue_subscription.go
|
||||||
linters:
|
linters:
|
||||||
- dupl
|
- dupl
|
||||||
@@ -125,22 +98,3 @@ issues:
|
|||||||
- path: models/update.go
|
- path: models/update.go
|
||||||
linters:
|
linters:
|
||||||
- unused
|
- unused
|
||||||
- path: cmd/dump.go
|
|
||||||
linters:
|
|
||||||
- dupl
|
|
||||||
- path: services/webhook/webhook.go
|
|
||||||
linters:
|
|
||||||
- structcheck
|
|
||||||
- text: "commentFormatting: put a space between `//` and comment text"
|
|
||||||
linters:
|
|
||||||
- gocritic
|
|
||||||
- text: "exitAfterDefer:"
|
|
||||||
linters:
|
|
||||||
- gocritic
|
|
||||||
- path: modules/graceful/manager_windows.go
|
|
||||||
linters:
|
|
||||||
- staticcheck
|
|
||||||
text: "svc.IsAnInteractiveSession is deprecated: Use IsWindowsService instead."
|
|
||||||
- path: models/user/openid.go
|
|
||||||
linters:
|
|
||||||
- golint
|
|
||||||
|
|||||||
8
.ignore
8
.ignore
@@ -1,8 +1,6 @@
|
|||||||
*.min.css
|
/vendor
|
||||||
*.min.js
|
/public/vendor/plugins
|
||||||
|
/public/vendor/assets
|
||||||
/modules/options/bindata.go
|
/modules/options/bindata.go
|
||||||
/modules/public/bindata.go
|
/modules/public/bindata.go
|
||||||
/modules/templates/bindata.go
|
/modules/templates/bindata.go
|
||||||
/public/vendor/plugins
|
|
||||||
/vendor
|
|
||||||
node_modules
|
|
||||||
|
|||||||
3
.npmrc
3
.npmrc
@@ -1,5 +1,2 @@
|
|||||||
audit=false
|
|
||||||
fund=false
|
|
||||||
update-notifier=false
|
|
||||||
package-lock=true
|
package-lock=true
|
||||||
save-exact=true
|
save-exact=true
|
||||||
|
|||||||
25
.revive.toml
Normal file
25
.revive.toml
Normal file
@@ -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]
|
||||||
23
.stylelintrc
23
.stylelintrc
@@ -1,31 +1,16 @@
|
|||||||
extends: stylelint-config-standard
|
extends: stylelint-config-standard
|
||||||
|
|
||||||
overrides:
|
ignoreFiles:
|
||||||
- files: ["**/*.less"]
|
- web_src/less/vendor/**/*
|
||||||
customSyntax: postcss-less
|
|
||||||
|
|
||||||
rules:
|
rules:
|
||||||
alpha-value-notation: null
|
|
||||||
at-rule-empty-line-before: null
|
at-rule-empty-line-before: null
|
||||||
block-closing-brace-empty-line-before: null
|
block-closing-brace-empty-line-before: null
|
||||||
color-function-notation: null
|
|
||||||
color-hex-length: null
|
color-hex-length: null
|
||||||
comment-empty-line-before: 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
|
declaration-empty-line-before: null
|
||||||
hue-degree-notation: null
|
indentation: 4
|
||||||
indentation: 2
|
|
||||||
max-line-length: null
|
|
||||||
no-descending-specificity: null
|
no-descending-specificity: null
|
||||||
no-invalid-position-at-import-rule: null
|
|
||||||
number-leading-zero: never
|
number-leading-zero: never
|
||||||
number-max-precision: null
|
|
||||||
property-no-vendor-prefix: null
|
|
||||||
rule-empty-line-before: null
|
rule-empty-line-before: null
|
||||||
selector-class-pattern: null
|
selector-pseudo-element-colon-notation: 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
|
|
||||||
|
|||||||
1814
CHANGELOG.md
1814
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
103
CONTRIBUTING.md
103
CONTRIBUTING.md
@@ -3,14 +3,12 @@
|
|||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
- [Contribution Guidelines](#contribution-guidelines)
|
- [Contribution Guidelines](#contribution-guidelines)
|
||||||
- [Table of Contents](#table-of-contents)
|
|
||||||
- [Introduction](#introduction)
|
- [Introduction](#introduction)
|
||||||
- [Bug reports](#bug-reports)
|
- [Bug reports](#bug-reports)
|
||||||
- [Discuss your design](#discuss-your-design)
|
- [Discuss your design](#discuss-your-design)
|
||||||
- [Testing redux](#testing-redux)
|
- [Testing redux](#testing-redux)
|
||||||
- [Vendoring](#vendoring)
|
- [Vendoring](#vendoring)
|
||||||
- [Translation](#translation)
|
- [Translation](#translation)
|
||||||
- [Building Gitea](#building-gitea)
|
|
||||||
- [Code review](#code-review)
|
- [Code review](#code-review)
|
||||||
- [Styleguide](#styleguide)
|
- [Styleguide](#styleguide)
|
||||||
- [Design guideline](#design-guideline)
|
- [Design guideline](#design-guideline)
|
||||||
@@ -81,22 +79,23 @@ Here's how to run the test suite:
|
|||||||
|``make lint-frontend`` | lint frontend files |
|
|``make lint-frontend`` | lint frontend files |
|
||||||
|``make lint-backend`` | lint backend files |
|
|``make lint-backend`` | lint backend files |
|
||||||
|
|
||||||
- run test code (Suggest run in Linux)
|
- run test code (Suggest run in linux)
|
||||||
|
|
||||||
| | |
|
| | |
|
||||||
| :------------------------------------- | :----------------------------------------------- |
|
| :------------------------------------- | :----------------------------------------------- |
|
||||||
|``make test[\#TestSpecificName]`` | run unit test |
|
|``make test[\#TestSpecificName]`` | run unit test |
|
||||||
|``make test-sqlite[\#TestSpecificName]``| run [integration](integrations) test for SQLite |
|
|``make test-sqlite[\#TestSpecificName]``| run [integration](integrations) test for sqlite |
|
||||||
|[More details about integrations](integrations/README.md) |
|
|[More detail message about integrations](integrations/README.md) |
|
||||||
|
|
||||||
## Vendoring
|
## Vendoring
|
||||||
|
|
||||||
We manage dependencies via [Go Modules](https://golang.org/cmd/go/#hdr-Module_maintenance), more details: [go mod](https://go.dev/ref/mod).
|
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 `go.mod`, `go.sum` updates if they are part of
|
Pull requests should only include `vendor/` updates if they are part of
|
||||||
the same change, be it a bugfix or a feature addition.
|
the same change, be it a bugfix or a feature addition.
|
||||||
|
|
||||||
The `go.mod`, `go.sum` update needs to be justified as part of the PR description,
|
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
|
and must be verified by the reviewers and/or merger to always reference
|
||||||
an existing upstream commit.
|
an existing upstream commit.
|
||||||
|
|
||||||
@@ -105,7 +104,7 @@ You can find more information on how to get started with it on the [Modules Wiki
|
|||||||
## Translation
|
## Translation
|
||||||
|
|
||||||
We do all translation work inside [Crowdin](https://crowdin.com/project/gitea).
|
We do all translation work inside [Crowdin](https://crowdin.com/project/gitea).
|
||||||
The only translation that is maintained in this Git repository is
|
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)
|
[`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
|
and is synced regularly to Crowdin. Once a translation has reached
|
||||||
A SATISFACTORY PERCENTAGE it will be synced back into this repo and
|
A SATISFACTORY PERCENTAGE it will be synced back into this repo and
|
||||||
@@ -156,10 +155,10 @@ import (
|
|||||||
|
|
||||||
## Design guideline
|
## 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:
|
To maintain understandable code and avoid circular dependencies it is important to have a good structure of the code. The gitea code is divided into the following parts:
|
||||||
|
|
||||||
- **integration:** Integrations tests
|
- **integration:** 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:** Contains the data structures used by xorm to construct database tables. It also contains supporting functions to query and update the database. Dependecies to other code in Gitea should be avoided although some modules might be needed (for example for logging).
|
||||||
- **models/fixtures:** Sample model data used in integration tests.
|
- **models/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.
|
- **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.
|
- **modules:** Different modules to handle specific functionality in Gitea.
|
||||||
@@ -182,16 +181,16 @@ The same applies to status responses. If you notice a problem, feel free to leav
|
|||||||
All expected results (errors, success, fail messages) should be documented
|
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)).
|
([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/)
|
All JSON input types must be defined as a struct in `models/structs/`
|
||||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/modules/structs/issue.go#L76-L91))
|
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/modules/structs/issue.go#L76-L91))
|
||||||
and referenced in
|
and referenced in
|
||||||
[routers/api/v1/swagger/options.go](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/swagger/options.go).
|
[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:
|
They can then be used like the following:
|
||||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L318)).
|
([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/)
|
All JSON responses must be defined as a struct in `models/structs/`
|
||||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/modules/structs/issue.go#L36-L68))
|
([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/)
|
and referenced in its category in `routers/api/v1/swagger/`
|
||||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/swagger/issue.go#L11-L16))
|
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/swagger/issue.go#L11-L16))
|
||||||
They can be used like the following:
|
They can be used like the following:
|
||||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L277-L279))
|
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L277-L279))
|
||||||
@@ -200,16 +199,12 @@ In general, HTTP methods are chosen as follows:
|
|||||||
* **GET** endpoints return requested object and status **OK (200)**
|
* **GET** endpoints return requested object and status **OK (200)**
|
||||||
* **DELETE** endpoints return status **No Content (204)**
|
* **DELETE** endpoints return status **No Content (204)**
|
||||||
* **POST** endpoints return status **Created (201)**, used to **create** new objects (e.g. a User)
|
* **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)
|
* **PUT** endpoints return status **No Content (204)**, used to **add/assign** existing Obejcts (e.g. User) to something (e.g. Org-Team)
|
||||||
* **PATCH** endpoints return changed object and status **OK (200)**, used to **edit/change** an existing object
|
* **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).
|
An endpoint which changes/edits an object expects all fields to be optional (except ones to identify the object, which are required).
|
||||||
|
|
||||||
### Endpoints returning lists should
|
|
||||||
* support pagination (`page` & `limit` options in query)
|
|
||||||
* set `X-Total-Count` header via **SetTotalCountHeader** ([example](https://github.com/go-gitea/gitea/blob/7aae98cc5d4113f1e9918b7ee7dd09f67c189e3e/routers/api/v1/repo/issue.go#L444))
|
|
||||||
|
|
||||||
|
|
||||||
## Developer Certificate of Origin (DCO)
|
## Developer Certificate of Origin (DCO)
|
||||||
|
|
||||||
@@ -222,7 +217,7 @@ Additionally you could add a line at the end of your commit message.
|
|||||||
Signed-off-by: Joe Smith <joe.smith@email.com>
|
Signed-off-by: Joe Smith <joe.smith@email.com>
|
||||||
```
|
```
|
||||||
|
|
||||||
If you set your `user.name` and `user.email` Git configs, you can add the
|
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`.
|
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.
|
We assume in good faith that the information you provide is legally binding.
|
||||||
@@ -231,18 +226,18 @@ We assume in good faith that the information you provide is legally binding.
|
|||||||
|
|
||||||
We adopted a release schedule to streamline the process of working
|
We adopted a release schedule to streamline the process of working
|
||||||
on, finishing, and issuing releases. The overall goal is to make a
|
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
|
minor release every two months, which breaks down into one month of
|
||||||
general development followed by one month of testing and polishing
|
general development followed by one month of testing and polishing
|
||||||
known as the release freeze. All the feature pull requests should be
|
known as the release freeze. All the feature pull requests should be
|
||||||
merged before feature freeze. And, during the frozen period, a corresponding
|
merged in the first month of one release period. And, during the frozen
|
||||||
release branch is open for fixes backported from main branch. Release candidates
|
period, a corresponding release branch is open for fixes backported from
|
||||||
are made during this period for user testing to
|
master. Release candidates are made during this period for user testing to
|
||||||
obtain a final version that is maintained in this branch. A release is
|
obtain a final version that is maintained in this branch. A release is
|
||||||
maintained by issuing patch releases to only correct critical problems
|
maintained by issuing patch releases to only correct critical problems
|
||||||
such as crashes or security issues.
|
such as crashes or security issues.
|
||||||
|
|
||||||
Major release cycles are seasonal. They always begin on the 25th and end on
|
Major release cycles are bimonthly. They always begin on the 25th and end on
|
||||||
the 24th (i.e., the 25th of December to March 24th).
|
the 24th (i.e., the 25th of December to February 24th).
|
||||||
|
|
||||||
During a development cycle, we may also publish any necessary minor releases
|
During a development cycle, we may also publish any necessary minor releases
|
||||||
for the previous version. For example, if the latest, published release is
|
for the previous version. For example, if the latest, published release is
|
||||||
@@ -267,7 +262,7 @@ 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
|
months and forgets to leave the maintainers team, the owners may move
|
||||||
him or her from the maintainers team to the advisors team.
|
him or her from the maintainers team to the advisors team.
|
||||||
For security reasons, Maintainers should use 2FA for their accounts and
|
For security reasons, Maintainers should use 2FA for their accounts and
|
||||||
if possible provide GPG signed commits.
|
if possible provide gpg signed commits.
|
||||||
https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/
|
https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/
|
||||||
https://help.github.com/articles/signing-commits-with-gpg/
|
https://help.github.com/articles/signing-commits-with-gpg/
|
||||||
|
|
||||||
@@ -298,45 +293,35 @@ and lead the development of Gitea.
|
|||||||
To honor the past owners, here's the history of the owners and the time
|
To honor the past owners, here's the history of the owners and the time
|
||||||
they served:
|
they served:
|
||||||
|
|
||||||
* 2022-01-01 ~ 2022-12-31 - https://github.com/go-gitea/gitea/issues/17872
|
|
||||||
* [Lunny Xiao](https://gitea.com/lunny) <xiaolunwen@gmail.com>
|
|
||||||
* [Matti Ranta](https://gitea.com/techknowlogick) <techknowlogick@gitea.io>
|
|
||||||
* [Andrew Thornton](https://gitea.com/zeripath) <art27@cantab.net>
|
|
||||||
|
|
||||||
* 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
|
* 2016-11-04 ~ 2017-12-31
|
||||||
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
||||||
* [Thomas Boerger](https://github.com/tboerger) <thomas@webhippie.de>
|
* [Thomas Boerger](https://github.com/tboerger) <thomas@webhippie.de>
|
||||||
* [Kim Carlbäcker](https://github.com/bkcsoft) <kim.carlbacker@gmail.com>
|
* [Kim Carlbäcker](https://github.com/bkcsoft) <kim.carlbacker@gmail.com>
|
||||||
|
|
||||||
|
* 2018-01-01 ~ 2018-12-31
|
||||||
|
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
||||||
|
* [Lauris Bukšis-Haberkorns](https://github.com/lafriks) <lauris@nix.lv>
|
||||||
|
* [Kim Carlbäcker](https://github.com/bkcsoft) <kim.carlbacker@gmail.com>
|
||||||
|
|
||||||
|
* 2019-01-01 ~ 2019-12-31
|
||||||
|
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
||||||
|
* [Lauris Bukšis-Haberkorns](https://github.com/lafriks) <lauris@nix.lv>
|
||||||
|
* [Matti Ranta](https://github.com/techknowlogick) <techknowlogick@gitea.io>
|
||||||
|
|
||||||
|
* 2020-01-01 ~ 2020-12-31
|
||||||
|
* [Lunny Xiao](https://gitea.com/lunny) <xiaolunwen@gmail.com>
|
||||||
|
* [Lauris Bukšis-Haberkorns](https://gitea.com/lafriks) <lauris@nix.lv>
|
||||||
|
* [Matti Ranta](https://gitea.com/techknowlogick) <techknowlogick@gitea.io>
|
||||||
|
|
||||||
## Versions
|
## Versions
|
||||||
|
|
||||||
Gitea has the `main` branch as a tip branch and has version branches
|
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
|
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
|
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,
|
pull requests on the `release/v0.9` branch and publish a `v0.9.1` tag,
|
||||||
after bringing the bug fix also to the main branch.
|
after bringing the bug fix also to the master branch.
|
||||||
|
|
||||||
Since the `main` branch is a tip version, if you wish to use Gitea
|
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
|
in production, please download the latest release tag version. All the
|
||||||
branches will be protected via GitHub, all the PRs to every branch must
|
branches will be protected via GitHub, all the PRs to every branch must
|
||||||
be reviewed by two maintainers and must pass the automatic tests.
|
be reviewed by two maintainers and must pass the automatic tests.
|
||||||
@@ -344,14 +329,14 @@ be reviewed by two maintainers and must pass the automatic tests.
|
|||||||
## Releasing Gitea
|
## 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.
|
* 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.
|
* 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 `main` with PRs with label `changelog` and after it has been merged do following steps:
|
* 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`.
|
* 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`
|
* 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.
|
* 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`.
|
* 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 create a release and upload all the compiled binary. (But currently it doesn't add the release notes automatically. Maybe we should fix that.)
|
* 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 `main`.
|
* If needed send PR for changelog on branch `master`.
|
||||||
* Send PR to [blog repository](https://gitea.com/gitea/blog) announcing the release.
|
* Send PR to [blog repository](https://gitea.com/gitea/blog) announcing the release.
|
||||||
|
|
||||||
## Copyright
|
## Copyright
|
||||||
|
|||||||
18
Dockerfile
18
Dockerfile
@@ -1,14 +1,14 @@
|
|||||||
|
|
||||||
###################################
|
###################################
|
||||||
#Build stage - temporarily using techknowlogick image until we upgrade to latest official alpine/go image
|
#Build stage
|
||||||
FROM techknowlogick/go:1.17-alpine3.13 AS build-env
|
FROM golang:1.14-alpine3.11 AS build-env
|
||||||
|
|
||||||
ARG GOPROXY
|
ARG GOPROXY
|
||||||
ENV GOPROXY ${GOPROXY:-direct}
|
ENV GOPROXY ${GOPROXY:-direct}
|
||||||
|
|
||||||
ARG GITEA_VERSION
|
ARG GITEA_VERSION
|
||||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
ARG TAGS="sqlite sqlite_unlock_notify"
|
||||||
ENV TAGS "bindata timetzdata $TAGS"
|
ENV TAGS "bindata $TAGS"
|
||||||
ARG CGO_EXTRA_CFLAGS
|
ARG CGO_EXTRA_CFLAGS
|
||||||
|
|
||||||
#Build deps
|
#Build deps
|
||||||
@@ -22,10 +22,7 @@ WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
|||||||
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
||||||
&& make clean-all build
|
&& make clean-all build
|
||||||
|
|
||||||
# Begin env-to-ini build
|
FROM alpine:3.11
|
||||||
RUN go build contrib/environment-to-ini/environment-to-ini.go
|
|
||||||
|
|
||||||
FROM alpine:3.13
|
|
||||||
LABEL maintainer="maintainers@gitea.io"
|
LABEL maintainer="maintainers@gitea.io"
|
||||||
|
|
||||||
EXPOSE 22 3000
|
EXPOSE 22 3000
|
||||||
@@ -41,6 +38,7 @@ RUN apk --no-cache add \
|
|||||||
s6 \
|
s6 \
|
||||||
sqlite \
|
sqlite \
|
||||||
su-exec \
|
su-exec \
|
||||||
|
tzdata \
|
||||||
gnupg
|
gnupg
|
||||||
|
|
||||||
RUN addgroup \
|
RUN addgroup \
|
||||||
@@ -53,7 +51,7 @@ RUN addgroup \
|
|||||||
-u 1000 \
|
-u 1000 \
|
||||||
-G git \
|
-G git \
|
||||||
git && \
|
git && \
|
||||||
echo "git:*" | chpasswd -e
|
echo "git:$(dd if=/dev/urandom bs=24 count=1 status=none | base64)" | chpasswd
|
||||||
|
|
||||||
ENV USER git
|
ENV USER git
|
||||||
ENV GITEA_CUSTOM /data/gitea
|
ENV GITEA_CUSTOM /data/gitea
|
||||||
@@ -65,6 +63,4 @@ CMD ["/bin/s6-svscan", "/etc/s6"]
|
|||||||
|
|
||||||
COPY docker/root /
|
COPY docker/root /
|
||||||
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
||||||
COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
RUN ln -s /app/gitea/gitea /usr/local/bin/gitea
|
||||||
RUN chmod 755 /usr/bin/entrypoint /app/gitea/gitea /usr/local/bin/gitea /usr/local/bin/environment-to-ini
|
|
||||||
RUN chmod 755 /etc/s6/gitea/* /etc/s6/openssh/* /etc/s6/.s6-svscan/*
|
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
|
|
||||||
###################################
|
|
||||||
#Build stage - temporarily using techknowlogick image until we upgrade to latest official alpine/go image
|
|
||||||
FROM techknowlogick/go:1.17-alpine3.13 AS build-env
|
|
||||||
|
|
||||||
ARG GOPROXY
|
|
||||||
ENV GOPROXY ${GOPROXY:-direct}
|
|
||||||
|
|
||||||
ARG GITEA_VERSION
|
|
||||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
|
||||||
ENV TAGS "bindata timetzdata $TAGS"
|
|
||||||
ARG CGO_EXTRA_CFLAGS
|
|
||||||
|
|
||||||
#Build deps
|
|
||||||
RUN apk --no-cache add build-base git nodejs npm
|
|
||||||
|
|
||||||
#Setup repo
|
|
||||||
COPY . ${GOPATH}/src/code.gitea.io/gitea
|
|
||||||
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
|
||||||
|
|
||||||
#Checkout version if set
|
|
||||||
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
|
||||||
&& make clean-all build
|
|
||||||
|
|
||||||
# Begin env-to-ini build
|
|
||||||
RUN go build contrib/environment-to-ini/environment-to-ini.go
|
|
||||||
|
|
||||||
FROM alpine:3.13
|
|
||||||
LABEL maintainer="maintainers@gitea.io"
|
|
||||||
|
|
||||||
EXPOSE 2222 3000
|
|
||||||
|
|
||||||
RUN apk --no-cache add \
|
|
||||||
bash \
|
|
||||||
ca-certificates \
|
|
||||||
gettext \
|
|
||||||
git \
|
|
||||||
curl \
|
|
||||||
gnupg
|
|
||||||
|
|
||||||
RUN addgroup \
|
|
||||||
-S -g 1000 \
|
|
||||||
git && \
|
|
||||||
adduser \
|
|
||||||
-S -H -D \
|
|
||||||
-h /var/lib/gitea/git \
|
|
||||||
-s /bin/bash \
|
|
||||||
-u 1000 \
|
|
||||||
-G git \
|
|
||||||
git
|
|
||||||
|
|
||||||
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/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
|
|
||||||
ENV GITEA_WORK_DIR /var/lib/gitea
|
|
||||||
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 (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"]
|
|
||||||
WORKDIR /var/lib/gitea
|
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
|
||||||
CMD []
|
|
||||||
|
|
||||||
12
MAINTAINERS
12
MAINTAINERS
@@ -1,4 +1,5 @@
|
|||||||
Alexey Makhov <amakhov@avito.ru> (@makhov)
|
Alexey Makhov <amakhov@avito.ru> (@makhov)
|
||||||
|
Andrey Nering <andrey.nering@gmail.com> (@andreynering)
|
||||||
Bo-Yi Wu <appleboy.tw@gmail.com> (@appleboy)
|
Bo-Yi Wu <appleboy.tw@gmail.com> (@appleboy)
|
||||||
Ethan Koenig <ethantkoenig@gmail.com> (@ethantkoenig)
|
Ethan Koenig <ethantkoenig@gmail.com> (@ethantkoenig)
|
||||||
Kees de Vries <bouwko@gmail.com> (@Bwko)
|
Kees de Vries <bouwko@gmail.com> (@Bwko)
|
||||||
@@ -35,14 +36,3 @@ Mura Li <typeless@ctli.io> (@typeless)
|
|||||||
6543 <6543@obermui.de> (@6543)
|
6543 <6543@obermui.de> (@6543)
|
||||||
jaqra <jaqra@hotmail.com> (@jaqra)
|
jaqra <jaqra@hotmail.com> (@jaqra)
|
||||||
David Svantesson <davidsvantesson@gmail.com> (@davidsvantesson)
|
David Svantesson <davidsvantesson@gmail.com> (@davidsvantesson)
|
||||||
a1012112796 <1012112796@qq.com> (@a1012112796)
|
|
||||||
Karl Heinz Marbaise <kama@soebes.de> (@khmarbaise)
|
|
||||||
Norwin Roosen <git@nroo.de> (@noerw)
|
|
||||||
Kyle Dumont <kdumontnu@gmail.com> (@kdumontnu)
|
|
||||||
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)
|
|
||||||
Gusted <williamzijl7@hotmail.com) (@Gusted)
|
|
||||||
singuliere <singuliere@autistici.org> (@singuliere)
|
|
||||||
|
|||||||
479
Makefile
479
Makefile
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
ifeq ($(USE_REPO_TEST_DIR),1)
|
ifeq ($(USE_REPO_TEST_DIR),1)
|
||||||
|
|
||||||
# This rule replaces the whole Makefile when we're trying to use /tmp repository temporary files
|
# This rule replaces the whole Makefile when we're trying to use /tmp repository temporary files
|
||||||
@@ -20,18 +21,14 @@ IMPORT := code.gitea.io/gitea
|
|||||||
export GO111MODULE=on
|
export GO111MODULE=on
|
||||||
|
|
||||||
GO ?= go
|
GO ?= go
|
||||||
|
SED_INPLACE := sed -i
|
||||||
SHASUM ?= shasum -a 256
|
SHASUM ?= shasum -a 256
|
||||||
HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
|
HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
|
||||||
COMMA := ,
|
COMMA := ,
|
||||||
|
|
||||||
XGO_VERSION := go-1.17.x
|
XGO_VERSION := go-1.14.x
|
||||||
MIN_GO_VERSION := 001016000
|
MIN_GO_VERSION := 001012000
|
||||||
MIN_NODE_VERSION := 012017000
|
MIN_NODE_VERSION := 010013000
|
||||||
MIN_GOLANGCI_LINT_VERSION := 001043000
|
|
||||||
|
|
||||||
DOCKER_IMAGE ?= gitea/gitea
|
|
||||||
DOCKER_TAG ?= latest
|
|
||||||
DOCKER_REF := $(DOCKER_IMAGE):$(DOCKER_TAG)
|
|
||||||
|
|
||||||
ifeq ($(HAS_GO), GO)
|
ifeq ($(HAS_GO), GO)
|
||||||
GOPATH ?= $(shell $(GO) env GOPATH)
|
GOPATH ?= $(shell $(GO) env GOPATH)
|
||||||
@@ -41,31 +38,30 @@ ifeq ($(HAS_GO), GO)
|
|||||||
CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS)
|
CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
ifeq ($(OS), Windows_NT)
|
ifeq ($(OS), Windows_NT)
|
||||||
GOFLAGS := -v -buildmode=exe
|
|
||||||
EXECUTABLE ?= gitea.exe
|
|
||||||
else ifeq ($(OS), Windows)
|
|
||||||
GOFLAGS := -v -buildmode=exe
|
|
||||||
EXECUTABLE ?= gitea.exe
|
EXECUTABLE ?= gitea.exe
|
||||||
else
|
else
|
||||||
GOFLAGS := -v
|
|
||||||
EXECUTABLE ?= gitea
|
EXECUTABLE ?= gitea
|
||||||
|
UNAME_S := $(shell uname -s)
|
||||||
|
ifeq ($(UNAME_S),Darwin)
|
||||||
|
SED_INPLACE := sed -i ''
|
||||||
|
endif
|
||||||
|
ifeq ($(UNAME_S),FreeBSD)
|
||||||
|
SED_INPLACE := sed -i ''
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(shell sed --version 2>/dev/null | grep -q GNU && echo gnu),gnu)
|
GOFMT ?= gofmt -s
|
||||||
SED_INPLACE := sed -i
|
|
||||||
else
|
|
||||||
SED_INPLACE := sed -i ''
|
|
||||||
endif
|
|
||||||
|
|
||||||
|
GOFLAGS := -v
|
||||||
EXTRA_GOFLAGS ?=
|
EXTRA_GOFLAGS ?=
|
||||||
|
|
||||||
MAKE_VERSION := $(shell $(MAKE) -v | head -n 1)
|
MAKE_VERSION := $(shell $(MAKE) -v | head -n 1)
|
||||||
MAKE_EVIDENCE_DIR := .make_evidence
|
MAKE_EVIDENCE_DIR := .make_evidence
|
||||||
|
|
||||||
ifeq ($(RACE_ENABLED),true)
|
ifneq ($(RACE_ENABLED),)
|
||||||
GOFLAGS += -race
|
GOTESTFLAGS ?= -race
|
||||||
GOTESTFLAGS += -race
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
STORED_VERSION_FILE := VERSION
|
STORED_VERSION_FILE := VERSION
|
||||||
@@ -77,7 +73,7 @@ else
|
|||||||
ifneq ($(DRONE_BRANCH),)
|
ifneq ($(DRONE_BRANCH),)
|
||||||
VERSION ?= $(subst release/v,,$(DRONE_BRANCH))
|
VERSION ?= $(subst release/v,,$(DRONE_BRANCH))
|
||||||
else
|
else
|
||||||
VERSION ?= main
|
VERSION ?= master
|
||||||
endif
|
endif
|
||||||
|
|
||||||
STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null)
|
STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null)
|
||||||
@@ -90,34 +86,21 @@ endif
|
|||||||
|
|
||||||
LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
|
LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
|
||||||
|
|
||||||
LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64
|
GO_PACKAGES ?= $(filter-out code.gitea.io/gitea/integrations/migration-test,$(filter-out code.gitea.io/gitea/integrations,$(shell $(GO) list -mod=vendor ./... | 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 ./... | grep -v /vendor/))
|
|
||||||
|
|
||||||
FOMANTIC_WORK_DIR := web_src/fomantic
|
|
||||||
|
|
||||||
WEBPACK_SOURCES := $(shell find web_src/js web_src/less -type f)
|
WEBPACK_SOURCES := $(shell find web_src/js web_src/less -type f)
|
||||||
WEBPACK_CONFIGS := webpack.config.js
|
WEBPACK_CONFIGS := webpack.config.js
|
||||||
WEBPACK_DEST := public/js/index.js public/css/index.css
|
WEBPACK_DEST := public/js/index.js public/css/index.css
|
||||||
WEBPACK_DEST_ENTRIES := public/js public/css public/fonts public/img/webpack public/serviceworker.js
|
WEBPACK_DEST_ENTRIES := public/js public/css public/fonts public/serviceworker.js
|
||||||
|
|
||||||
BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
|
BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
|
||||||
BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))
|
BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))
|
||||||
|
|
||||||
SVG_DEST_DIR := public/img/svg
|
|
||||||
|
|
||||||
AIR_TMP_DIR := .air
|
|
||||||
|
|
||||||
TAGS ?=
|
TAGS ?=
|
||||||
TAGS_SPLIT := $(subst $(COMMA), ,$(TAGS))
|
TAGS_SPLIT := $(subst $(COMMA), ,$(TAGS))
|
||||||
TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
|
TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
|
||||||
|
|
||||||
TEST_TAGS ?= sqlite sqlite_unlock_notify
|
GO_DIRS := cmd integrations models modules routers build services vendor
|
||||||
|
|
||||||
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 integrations models modules routers build services tools
|
|
||||||
|
|
||||||
GO_SOURCES := $(wildcard *.go)
|
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 += $(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)
|
||||||
|
|
||||||
@@ -125,12 +108,17 @@ ifeq ($(filter $(TAGS_SPLIT),bindata),bindata)
|
|||||||
GO_SOURCES += $(BINDATA_DEST)
|
GO_SOURCES += $(BINDATA_DEST)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
#To update swagger use: GO111MODULE=on go get -u github.com/go-swagger/go-swagger/cmd/swagger
|
GO_SOURCES_OWN := $(filter-out vendor/% %/bindata.go, $(GO_SOURCES))
|
||||||
SWAGGER := $(GO) run github.com/go-swagger/go-swagger/cmd/swagger
|
|
||||||
|
FOMANTIC_CONFIGS := semantic.json web_src/fomantic/theme.config.less web_src/fomantic/_site/globals/site.variables web_src/fomantic/css.js
|
||||||
|
FOMANTIC_DEST := public/fomantic/semantic.min.js public/fomantic/semantic.min.css
|
||||||
|
FOMANTIC_DEST_DIR := public/fomantic
|
||||||
|
|
||||||
|
#To update swagger use: GO111MODULE=on go get -u github.com/go-swagger/go-swagger/cmd/swagger@v0.20.1
|
||||||
|
SWAGGER := $(GO) run -mod=vendor github.com/go-swagger/go-swagger/cmd/swagger
|
||||||
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
|
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_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl}}/api/v1"|g
|
||||||
SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|"basePath": "/api/v1"|g
|
SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl}}/api/v1"|"basePath": "/api/v1"|g
|
||||||
SWAGGER_EXCLUDE := code.gitea.io/sdk
|
|
||||||
SWAGGER_NEWLINE_COMMAND := -e '$$a\'
|
SWAGGER_NEWLINE_COMMAND := -e '$$a\'
|
||||||
|
|
||||||
TEST_MYSQL_HOST ?= mysql:3306
|
TEST_MYSQL_HOST ?= mysql:3306
|
||||||
@@ -154,48 +142,40 @@ TEST_MSSQL_PASSWORD ?= MwantsaSecurePassword1
|
|||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: build
|
all: build
|
||||||
|
|
||||||
|
include docker/Makefile
|
||||||
|
|
||||||
.PHONY: help
|
.PHONY: help
|
||||||
help:
|
help:
|
||||||
@echo "Make Routines:"
|
@echo "Make Routines:"
|
||||||
@echo " - \"\" equivalent to \"build\""
|
@echo " - \"\" equivalent to \"build\""
|
||||||
@echo " - build build everything"
|
@echo " - build build everything"
|
||||||
@echo " - frontend build frontend files"
|
@echo " - frontend build frontend files"
|
||||||
@echo " - backend build backend files"
|
@echo " - backend build backend files"
|
||||||
@echo " - watch watch everything and continuously rebuild"
|
|
||||||
@echo " - watch-frontend watch frontend files and continuously rebuild"
|
|
||||||
@echo " - watch-backend watch backend files and continuously rebuild"
|
|
||||||
@echo " - clean delete backend and integration files"
|
@echo " - clean delete backend and integration files"
|
||||||
@echo " - clean-all delete backend, frontend and integration files"
|
@echo " - clean-all delete backend, frontend and integration files"
|
||||||
@echo " - lint lint everything"
|
@echo " - lint lint everything"
|
||||||
@echo " - lint-frontend lint frontend files"
|
@echo " - lint-frontend lint frontend files"
|
||||||
@echo " - lint-backend lint backend files"
|
@echo " - lint-backend lint backend files"
|
||||||
@echo " - checks run various consistency checks"
|
@echo " - watch-frontend watch frontend files and continuously rebuild"
|
||||||
@echo " - checks-frontend check frontend files"
|
|
||||||
@echo " - checks-backend check backend files"
|
|
||||||
@echo " - test test everything"
|
|
||||||
@echo " - test-frontend test frontend files"
|
|
||||||
@echo " - test-backend test backend files"
|
|
||||||
@echo " - webpack build webpack files"
|
@echo " - webpack build webpack files"
|
||||||
@echo " - svg build svg files"
|
|
||||||
@echo " - fomantic build fomantic files"
|
@echo " - fomantic build fomantic files"
|
||||||
@echo " - generate run \"go generate\""
|
@echo " - generate run \"go generate\""
|
||||||
@echo " - fmt format the Go code"
|
@echo " - fmt format the Go code"
|
||||||
@echo " - generate-license update license files"
|
|
||||||
@echo " - generate-gitignore update gitignore files"
|
|
||||||
@echo " - generate-manpage generate manpage"
|
|
||||||
@echo " - generate-swagger generate the swagger spec from code comments"
|
@echo " - generate-swagger generate the swagger spec from code comments"
|
||||||
@echo " - swagger-validate check if the swagger spec is valid"
|
@echo " - swagger-validate check if the swagger spec is valid"
|
||||||
@echo " - golangci-lint run golangci-lint linter"
|
@echo " - golangci-lint run golangci-lint linter"
|
||||||
|
@echo " - revive run revive linter"
|
||||||
|
@echo " - misspell check for misspellings"
|
||||||
@echo " - vet examines Go source code and reports suspicious constructs"
|
@echo " - vet examines Go source code and reports suspicious constructs"
|
||||||
@echo " - test[\#TestSpecificName] run unit test"
|
@echo " - test[\#TestSpecificName] run unit test"
|
||||||
@echo " - test-sqlite[\#TestSpecificName] run integration test for sqlite"
|
@echo " - test-sqlite[\#TestSpecificName] run integration test for sqlite"
|
||||||
@echo " - pr#<index> build and start gitea from a PR with integration test data loaded"
|
@echo " - pr#<index> build and start gitea from a PR with integration test data loaded"
|
||||||
|
|
||||||
.PHONY: go-check
|
.PHONY: go-check
|
||||||
go-check:
|
go-check:
|
||||||
$(eval GO_VERSION := $(shell printf "%03d%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 \
|
@if [ "$(GO_VERSION)" -lt "$(MIN_GO_VERSION)" ]; then \
|
||||||
echo "Gitea requires Go 1.16 or greater to build. You can get it at https://golang.org/dl/"; \
|
echo "Gitea requires Go 1.12 or greater to build. You can get it at https://golang.org/dl/"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -209,16 +189,15 @@ git-check:
|
|||||||
.PHONY: node-check
|
.PHONY: node-check
|
||||||
node-check:
|
node-check:
|
||||||
$(eval NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v | cut -c2- | 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))
|
$(eval NPM_MISSING := $(shell hash npm > /dev/null 2>&1 || echo 1))
|
||||||
@if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" -o "$(NPM_MISSING)" = "1" ]; then \
|
@if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" -o "$(NPM_MISSING)" = "1" ]; then \
|
||||||
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/"; \
|
echo "Gitea requires Node.js 10 or greater and npm to build. You can get it at https://nodejs.org/en/download/"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
.PHONY: clean-all
|
.PHONY: clean-all
|
||||||
clean-all: clean
|
clean-all: clean
|
||||||
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules
|
rm -rf $(WEBPACK_DEST_ENTRIES) $(FOMANTIC_DEST_DIR)
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
@@ -227,18 +206,19 @@ clean:
|
|||||||
integrations*.test \
|
integrations*.test \
|
||||||
integrations/gitea-integration-pgsql/ integrations/gitea-integration-mysql/ integrations/gitea-integration-mysql8/ integrations/gitea-integration-sqlite/ \
|
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/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/
|
integrations/indexers-mssql integrations/mysql.ini integrations/mysql8.ini integrations/pgsql.ini integrations/mssql.ini
|
||||||
|
|
||||||
.PHONY: fmt
|
.PHONY: fmt
|
||||||
fmt:
|
fmt:
|
||||||
@echo "Running gitea-fmt(with gofmt)..."
|
$(GOFMT) -w $(GO_SOURCES_OWN)
|
||||||
@$(GO) run build/code-batch-process.go gitea-fmt -s -w '{file-list}'
|
|
||||||
|
|
||||||
.PHONY: vet
|
.PHONY: vet
|
||||||
vet:
|
vet:
|
||||||
@echo "Running go vet..."
|
# Default vet
|
||||||
@GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet
|
$(GO) vet $(GO_PACKAGES)
|
||||||
@$(GO) vet -vettool=gitea-vet $(GO_PACKAGES)
|
# Custom vet
|
||||||
|
$(GO) build -mod=vendor gitea.com/jolheiser/gitea-vet
|
||||||
|
$(GO) vet -vettool=gitea-vet $(GO_PACKAGES)
|
||||||
|
|
||||||
.PHONY: $(TAGS_EVIDENCE)
|
.PHONY: $(TAGS_EVIDENCE)
|
||||||
$(TAGS_EVIDENCE):
|
$(TAGS_EVIDENCE):
|
||||||
@@ -251,7 +231,7 @@ endif
|
|||||||
|
|
||||||
.PHONY: generate-swagger
|
.PHONY: generate-swagger
|
||||||
generate-swagger:
|
generate-swagger:
|
||||||
$(SWAGGER) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
|
$(SWAGGER) generate spec -o './$(SWAGGER_SPEC)'
|
||||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||||
$(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
|
$(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
|
||||||
|
|
||||||
@@ -262,7 +242,7 @@ swagger-check: generate-swagger
|
|||||||
echo "Please run 'make generate-swagger' and commit the result:"; \
|
echo "Please run 'make generate-swagger' and commit the result:"; \
|
||||||
echo "$${diff}"; \
|
echo "$${diff}"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi;
|
||||||
|
|
||||||
.PHONY: swagger-validate
|
.PHONY: swagger-validate
|
||||||
swagger-validate:
|
swagger-validate:
|
||||||
@@ -273,111 +253,94 @@ swagger-validate:
|
|||||||
.PHONY: errcheck
|
.PHONY: errcheck
|
||||||
errcheck:
|
errcheck:
|
||||||
@hash errcheck > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
@hash errcheck > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
$(GO) install github.com/kisielk/errcheck@8ddee489636a8311a376fc92e27a6a13c6658344; \
|
$(GO) get -u github.com/kisielk/errcheck; \
|
||||||
fi
|
fi
|
||||||
@echo "Running errcheck..."
|
errcheck $(GO_PACKAGES)
|
||||||
@errcheck $(GO_PACKAGES)
|
|
||||||
|
.PHONY: revive
|
||||||
|
revive:
|
||||||
|
GO111MODULE=on $(GO) run -mod=vendor build/lint.go -config .revive.toml -exclude=./vendor/... ./... || exit 1
|
||||||
|
|
||||||
|
.PHONY: misspell-check
|
||||||
|
misspell-check:
|
||||||
|
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
|
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
||||||
|
fi
|
||||||
|
misspell -error -i unknwon,destory $(GO_SOURCES_OWN)
|
||||||
|
|
||||||
|
.PHONY: misspell
|
||||||
|
misspell:
|
||||||
|
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
|
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
||||||
|
fi
|
||||||
|
misspell -w -i unknwon $(GO_SOURCES_OWN)
|
||||||
|
|
||||||
.PHONY: fmt-check
|
.PHONY: fmt-check
|
||||||
fmt-check:
|
fmt-check:
|
||||||
# get all go files and run gitea-fmt (with gofmt) on them
|
# get all go files and run go fmt on them
|
||||||
@diff=$$($(GO) run build/code-batch-process.go gitea-fmt -s -d '{file-list}'); \
|
@diff=$$($(GOFMT) -d $(GO_SOURCES_OWN)); \
|
||||||
if [ -n "$$diff" ]; then \
|
if [ -n "$$diff" ]; then \
|
||||||
echo "Please run 'make fmt' and commit the result:"; \
|
echo "Please run 'make fmt' and commit the result:"; \
|
||||||
echo "$${diff}"; \
|
echo "$${diff}"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi;
|
||||||
|
|
||||||
.PHONY: checks
|
|
||||||
checks: checks-frontend checks-backend
|
|
||||||
|
|
||||||
.PHONY: checks-frontend
|
|
||||||
checks-frontend: lockfile-check svg-check
|
|
||||||
|
|
||||||
.PHONY: checks-backend
|
|
||||||
checks-backend: gomod-check swagger-check swagger-validate
|
|
||||||
|
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint: lint-frontend lint-backend
|
lint: lint-backend lint-frontend
|
||||||
|
|
||||||
|
.PHONY: lint-backend
|
||||||
|
lint-backend: golangci-lint revive vet swagger-check swagger-validate test-vendor
|
||||||
|
|
||||||
.PHONY: lint-frontend
|
.PHONY: lint-frontend
|
||||||
lint-frontend: node_modules
|
lint-frontend: node_modules
|
||||||
npx eslint --color --max-warnings=0 web_src/js build templates *.config.js docs/assets/js
|
npx eslint web_src/js webpack.config.js
|
||||||
npx stylelint --color --max-warnings=0 web_src/less
|
npx stylelint web_src/less
|
||||||
npx editorconfig-checker templates
|
|
||||||
|
|
||||||
.PHONY: lint-backend
|
|
||||||
lint-backend: golangci-lint vet
|
|
||||||
|
|
||||||
.PHONY: watch
|
|
||||||
watch:
|
|
||||||
bash tools/watch.sh
|
|
||||||
|
|
||||||
.PHONY: watch-frontend
|
.PHONY: watch-frontend
|
||||||
watch-frontend: node-check node_modules
|
watch-frontend: node_modules
|
||||||
rm -rf $(WEBPACK_DEST_ENTRIES)
|
rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||||
NODE_ENV=development npx webpack --watch --progress
|
NODE_ENV=development npx webpack --hide-modules --display-entrypoints=false --watch --progress
|
||||||
|
|
||||||
.PHONY: watch-backend
|
|
||||||
watch-backend: go-check
|
|
||||||
@hash air > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
$(GO) install github.com/cosmtrek/air@bedc18201271882c2be66d216d0e1a275b526ec4; \
|
|
||||||
fi
|
|
||||||
air -c .air.toml
|
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test: test-frontend test-backend
|
test:
|
||||||
|
$(GO) test $(GOTESTFLAGS) -mod=vendor -tags='sqlite sqlite_unlock_notify' $(GO_PACKAGES)
|
||||||
.PHONY: test-backend
|
|
||||||
test-backend:
|
|
||||||
@echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
|
|
||||||
@$(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_PACKAGES)
|
|
||||||
|
|
||||||
.PHONY: test-frontend
|
|
||||||
test-frontend: node_modules
|
|
||||||
@NODE_OPTIONS="--experimental-vm-modules --no-warnings" npx jest --color
|
|
||||||
|
|
||||||
.PHONY: test-check
|
.PHONY: test-check
|
||||||
test-check:
|
test-check:
|
||||||
@echo "Running test-check...";
|
@echo "Checking if tests have changed the source tree...";
|
||||||
@diff=$$(git status -s); \
|
@diff=$$(git status -s); \
|
||||||
if [ -n "$$diff" ]; then \
|
if [ -n "$$diff" ]; then \
|
||||||
echo "make test-backend has changed files in the source tree:"; \
|
echo "make test has changed files in the source tree:"; \
|
||||||
echo "$${diff}"; \
|
echo "$${diff}"; \
|
||||||
echo "You should change the tests to create these files in a temporary directory."; \
|
echo "You should change the tests to create these files in a temporary directory."; \
|
||||||
echo "Do not simply add these files to .gitignore"; \
|
echo "Do not simply add these files to .gitignore"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi;
|
||||||
|
|
||||||
.PHONY: test\#%
|
.PHONY: test\#%
|
||||||
test\#%:
|
test\#%:
|
||||||
@echo "Running go test with -tags '$(TEST_TAGS)'..."
|
$(GO) test -mod=vendor -tags='sqlite sqlite_unlock_notify' -run $(subst .,/,$*) $(GO_PACKAGES)
|
||||||
@$(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -run $(subst .,/,$*) $(GO_PACKAGES)
|
|
||||||
|
|
||||||
.PHONY: coverage
|
.PHONY: coverage
|
||||||
coverage:
|
coverage:
|
||||||
grep '^\(mode: .*\)\|\(.*:[0-9]\+\.[0-9]\+,[0-9]\+\.[0-9]\+ [0-9]\+ [0-9]\+\)$$' coverage.out > coverage-bodged.out
|
GO111MODULE=on $(GO) run -mod=vendor build/gocovmerge.go integration.coverage.out $(shell find . -type f -name "coverage.out") > coverage.all
|
||||||
grep '^\(mode: .*\)\|\(.*:[0-9]\+\.[0-9]\+,[0-9]\+\.[0-9]\+ [0-9]\+ [0-9]\+\)$$' integration.coverage.out > integration.coverage-bodged.out
|
|
||||||
GO111MODULE=on $(GO) run build/gocovmerge.go integration.coverage-bodged.out coverage-bodged.out > coverage.all || (echo "gocovmerge failed"; echo "integration.coverage.out"; cat integration.coverage.out; echo "coverage.out"; cat coverage.out; exit 1)
|
|
||||||
|
|
||||||
.PHONY: unit-test-coverage
|
.PHONY: unit-test-coverage
|
||||||
unit-test-coverage:
|
unit-test-coverage:
|
||||||
@echo "Running unit-test-coverage $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
|
$(GO) test $(GOTESTFLAGS) -mod=vendor -tags='sqlite sqlite_unlock_notify' -cover -coverprofile coverage.out $(GO_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
|
||||||
@$(GO) test $(GOTESTFLAGS) -timeout=20m -tags='$(TEST_TAGS)' -cover -coverprofile coverage.out $(GO_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
|
|
||||||
|
|
||||||
.PHONY: vendor
|
.PHONY: vendor
|
||||||
vendor:
|
vendor:
|
||||||
$(GO) mod tidy && $(GO) mod vendor
|
$(GO) mod tidy && $(GO) mod vendor
|
||||||
|
|
||||||
.PHONY: gomod-check
|
.PHONY: test-vendor
|
||||||
gomod-check:
|
test-vendor: vendor
|
||||||
@$(GO) mod tidy
|
@diff=$$(git diff vendor/); \
|
||||||
@diff=$$(git diff go.sum); \
|
|
||||||
if [ -n "$$diff" ]; then \
|
if [ -n "$$diff" ]; then \
|
||||||
echo "Please run '$(GO) mod tidy' and commit the result:"; \
|
echo "Please run 'make vendor' and commit the result:"; \
|
||||||
echo "$${diff}"; \
|
echo "$${diff}"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi;
|
||||||
|
|
||||||
generate-ini-sqlite:
|
generate-ini-sqlite:
|
||||||
sed -e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
sed -e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||||
@@ -385,16 +348,15 @@ generate-ini-sqlite:
|
|||||||
|
|
||||||
.PHONY: test-sqlite
|
.PHONY: test-sqlite
|
||||||
test-sqlite: integrations.sqlite.test generate-ini-sqlite
|
test-sqlite: integrations.sqlite.test generate-ini-sqlite
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test
|
||||||
|
|
||||||
.PHONY: test-sqlite\#%
|
.PHONY: test-sqlite\#%
|
||||||
test-sqlite\#%: integrations.sqlite.test generate-ini-sqlite
|
test-sqlite\#%: integrations.sqlite.test generate-ini-sqlite
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.run $(subst .,/,$*)
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.run $(subst .,/,$*)
|
||||||
|
|
||||||
.PHONY: test-sqlite-migration
|
.PHONY: test-sqlite-migration
|
||||||
test-sqlite-migration: migrations.sqlite.test migrations.individual.sqlite.test generate-ini-sqlite
|
test-sqlite-migration: migrations.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.sqlite.test
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./migrations.individual.sqlite.test
|
|
||||||
|
|
||||||
generate-ini-mysql:
|
generate-ini-mysql:
|
||||||
sed -e 's|{{TEST_MYSQL_HOST}}|${TEST_MYSQL_HOST}|g' \
|
sed -e 's|{{TEST_MYSQL_HOST}}|${TEST_MYSQL_HOST}|g' \
|
||||||
@@ -406,16 +368,15 @@ generate-ini-mysql:
|
|||||||
|
|
||||||
.PHONY: test-mysql
|
.PHONY: test-mysql
|
||||||
test-mysql: integrations.mysql.test generate-ini-mysql
|
test-mysql: integrations.mysql.test generate-ini-mysql
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test
|
||||||
|
|
||||||
.PHONY: test-mysql\#%
|
.PHONY: test-mysql\#%
|
||||||
test-mysql\#%: integrations.mysql.test generate-ini-mysql
|
test-mysql\#%: integrations.mysql.test generate-ini-mysql
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test -test.run $(subst .,/,$*)
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test -test.run $(subst .,/,$*)
|
||||||
|
|
||||||
.PHONY: test-mysql-migration
|
.PHONY: test-mysql-migration
|
||||||
test-mysql-migration: migrations.mysql.test migrations.individual.mysql.test generate-ini-mysql
|
test-mysql-migration: migrations.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.mysql.test
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./migrations.individual.mysql.test
|
|
||||||
|
|
||||||
generate-ini-mysql8:
|
generate-ini-mysql8:
|
||||||
sed -e 's|{{TEST_MYSQL8_HOST}}|${TEST_MYSQL8_HOST}|g' \
|
sed -e 's|{{TEST_MYSQL8_HOST}}|${TEST_MYSQL8_HOST}|g' \
|
||||||
@@ -427,16 +388,15 @@ generate-ini-mysql8:
|
|||||||
|
|
||||||
.PHONY: test-mysql8
|
.PHONY: test-mysql8
|
||||||
test-mysql8: integrations.mysql8.test generate-ini-mysql8
|
test-mysql8: integrations.mysql8.test generate-ini-mysql8
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./integrations.mysql8.test
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql8.ini ./integrations.mysql8.test
|
||||||
|
|
||||||
.PHONY: test-mysql8\#%
|
.PHONY: test-mysql8\#%
|
||||||
test-mysql8\#%: integrations.mysql8.test generate-ini-mysql8
|
test-mysql8\#%: integrations.mysql8.test generate-ini-mysql8
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./integrations.mysql8.test -test.run $(subst .,/,$*)
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql8.ini ./integrations.mysql8.test -test.run $(subst .,/,$*)
|
||||||
|
|
||||||
.PHONY: test-mysql8-migration
|
.PHONY: test-mysql8-migration
|
||||||
test-mysql8-migration: migrations.mysql8.test migrations.individual.mysql8.test generate-ini-mysql8
|
test-mysql8-migration: migrations.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.mysql8.test
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./migrations.individual.mysql8.test
|
|
||||||
|
|
||||||
generate-ini-pgsql:
|
generate-ini-pgsql:
|
||||||
sed -e 's|{{TEST_PGSQL_HOST}}|${TEST_PGSQL_HOST}|g' \
|
sed -e 's|{{TEST_PGSQL_HOST}}|${TEST_PGSQL_HOST}|g' \
|
||||||
@@ -449,16 +409,15 @@ generate-ini-pgsql:
|
|||||||
|
|
||||||
.PHONY: test-pgsql
|
.PHONY: test-pgsql
|
||||||
test-pgsql: integrations.pgsql.test generate-ini-pgsql
|
test-pgsql: integrations.pgsql.test generate-ini-pgsql
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test
|
||||||
|
|
||||||
.PHONY: test-pgsql\#%
|
.PHONY: test-pgsql\#%
|
||||||
test-pgsql\#%: integrations.pgsql.test generate-ini-pgsql
|
test-pgsql\#%: integrations.pgsql.test generate-ini-pgsql
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test -test.run $(subst .,/,$*)
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test -test.run $(subst .,/,$*)
|
||||||
|
|
||||||
.PHONY: test-pgsql-migration
|
.PHONY: test-pgsql-migration
|
||||||
test-pgsql-migration: migrations.pgsql.test migrations.individual.pgsql.test generate-ini-pgsql
|
test-pgsql-migration: migrations.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.pgsql.test
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./migrations.individual.pgsql.test
|
|
||||||
|
|
||||||
generate-ini-mssql:
|
generate-ini-mssql:
|
||||||
sed -e 's|{{TEST_MSSQL_HOST}}|${TEST_MSSQL_HOST}|g' \
|
sed -e 's|{{TEST_MSSQL_HOST}}|${TEST_MSSQL_HOST}|g' \
|
||||||
@@ -470,54 +429,53 @@ generate-ini-mssql:
|
|||||||
|
|
||||||
.PHONY: test-mssql
|
.PHONY: test-mssql
|
||||||
test-mssql: integrations.mssql.test generate-ini-mssql
|
test-mssql: integrations.mssql.test generate-ini-mssql
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test
|
||||||
|
|
||||||
.PHONY: test-mssql\#%
|
.PHONY: test-mssql\#%
|
||||||
test-mssql\#%: integrations.mssql.test generate-ini-mssql
|
test-mssql\#%: integrations.mssql.test generate-ini-mssql
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test -test.run $(subst .,/,$*)
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test -test.run $(subst .,/,$*)
|
||||||
|
|
||||||
.PHONY: test-mssql-migration
|
.PHONY: test-mssql-migration
|
||||||
test-mssql-migration: migrations.mssql.test migrations.individual.mssql.test generate-ini-mssql
|
test-mssql-migration: migrations.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.mssql.test
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./migrations.individual.mssql.test -test.failfast
|
|
||||||
|
|
||||||
.PHONY: bench-sqlite
|
.PHONY: bench-sqlite
|
||||||
bench-sqlite: integrations.sqlite.test generate-ini-sqlite
|
bench-sqlite: integrations.sqlite.test generate-ini-sqlite
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||||
|
|
||||||
.PHONY: bench-mysql
|
.PHONY: bench-mysql
|
||||||
bench-mysql: integrations.mysql.test generate-ini-mysql
|
bench-mysql: integrations.mysql.test generate-ini-mysql
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||||
|
|
||||||
.PHONY: bench-mssql
|
.PHONY: bench-mssql
|
||||||
bench-mssql: integrations.mssql.test generate-ini-mssql
|
bench-mssql: integrations.mssql.test generate-ini-mssql
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||||
|
|
||||||
.PHONY: bench-pgsql
|
.PHONY: bench-pgsql
|
||||||
bench-pgsql: integrations.pgsql.test generate-ini-pgsql
|
bench-pgsql: integrations.pgsql.test generate-ini-pgsql
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||||
|
|
||||||
.PHONY: integration-test-coverage
|
.PHONY: integration-test-coverage
|
||||||
integration-test-coverage: integrations.cover.test generate-ini-mysql
|
integration-test-coverage: integrations.cover.test generate-ini-mysql
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.cover.test -test.coverprofile=integration.coverage.out
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.cover.test -test.coverprofile=integration.coverage.out
|
||||||
|
|
||||||
integrations.mysql.test: git-check $(GO_SOURCES)
|
integrations.mysql.test: git-check $(GO_SOURCES)
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -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)
|
integrations.mysql8.test: git-check $(GO_SOURCES)
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -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)
|
integrations.pgsql.test: git-check $(GO_SOURCES)
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -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)
|
integrations.mssql.test: git-check $(GO_SOURCES)
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -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)
|
integrations.sqlite.test: git-check $(GO_SOURCES)
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -o integrations.sqlite.test -tags '$(TEST_TAGS)'
|
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.sqlite.test -tags 'sqlite sqlite_unlock_notify'
|
||||||
|
|
||||||
integrations.cover.test: git-check $(GO_SOURCES)
|
integrations.cover.test: git-check $(GO_SOURCES)
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -coverpkg $(shell echo $(GO_PACKAGES) | tr ' ' ',') -o integrations.cover.test
|
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -coverpkg $(shell echo $(GO_PACKAGES) | tr ' ' ',') -o integrations.cover.test
|
||||||
|
|
||||||
.PHONY: migrations.mysql.test
|
.PHONY: migrations.mysql.test
|
||||||
migrations.mysql.test: $(GO_SOURCES)
|
migrations.mysql.test: $(GO_SOURCES)
|
||||||
@@ -537,27 +495,7 @@ migrations.mssql.test: $(GO_SOURCES)
|
|||||||
|
|
||||||
.PHONY: migrations.sqlite.test
|
.PHONY: migrations.sqlite.test
|
||||||
migrations.sqlite.test: $(GO_SOURCES)
|
migrations.sqlite.test: $(GO_SOURCES)
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.sqlite.test -tags '$(TEST_TAGS)'
|
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.sqlite.test -tags 'sqlite sqlite_unlock_notify'
|
||||||
|
|
||||||
.PHONY: migrations.individual.mysql.test
|
|
||||||
migrations.individual.mysql.test: $(GO_SOURCES)
|
|
||||||
$(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)
|
|
||||||
$(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)
|
|
||||||
$(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)
|
|
||||||
$(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)
|
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/models/migrations -o migrations.individual.sqlite.test -tags '$(TEST_TAGS)'
|
|
||||||
|
|
||||||
.PHONY: check
|
.PHONY: check
|
||||||
check: test
|
check: test
|
||||||
@@ -570,21 +508,20 @@ install: $(wildcard *.go)
|
|||||||
build: frontend backend
|
build: frontend backend
|
||||||
|
|
||||||
.PHONY: frontend
|
.PHONY: frontend
|
||||||
frontend: $(WEBPACK_DEST)
|
frontend: node-check $(FOMANTIC_DEST) $(WEBPACK_DEST)
|
||||||
|
|
||||||
.PHONY: backend
|
.PHONY: backend
|
||||||
backend: go-check generate $(EXECUTABLE)
|
backend: go-check generate $(EXECUTABLE)
|
||||||
|
|
||||||
.PHONY: generate
|
.PHONY: generate
|
||||||
generate: $(TAGS_PREREQ)
|
generate: $(TAGS_PREREQ)
|
||||||
@echo "Running go generate..."
|
CC= GOOS= GOARCH= $(GO) generate -mod=vendor -tags '$(TAGS)' $(GO_PACKAGES)
|
||||||
@CC= GOOS= GOARCH= $(GO) generate -tags '$(TAGS)' $(GO_PACKAGES)
|
|
||||||
|
|
||||||
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
|
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
|
||||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build -mod=vendor $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||||
|
|
||||||
.PHONY: release
|
.PHONY: release
|
||||||
release: frontend generate release-windows release-linux release-darwin release-copy release-compress vendor release-sources release-docs release-check
|
release: frontend generate release-windows release-linux release-darwin release-copy release-compress release-sources release-check
|
||||||
|
|
||||||
$(DIST_DIRS):
|
$(DIST_DIRS):
|
||||||
mkdir -p $(DIST_DIRS)
|
mkdir -p $(DIST_DIRS)
|
||||||
@@ -592,12 +529,9 @@ $(DIST_DIRS):
|
|||||||
.PHONY: release-windows
|
.PHONY: release-windows
|
||||||
release-windows: | $(DIST_DIRS)
|
release-windows: | $(DIST_DIRS)
|
||||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
$(GO) get -u src.techknowlogick.com/xgo; \
|
||||||
fi
|
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) .
|
CGO_CFLAGS="$(CGO_CFLAGS)" GO111MODULE=off xgo -go $(XGO_VERSION) -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)" 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),drone)
|
ifeq ($(CI),drone)
|
||||||
cp /build/* $(DIST)/binaries
|
cp /build/* $(DIST)/binaries
|
||||||
endif
|
endif
|
||||||
@@ -605,9 +539,9 @@ endif
|
|||||||
.PHONY: release-linux
|
.PHONY: release-linux
|
||||||
release-linux: | $(DIST_DIRS)
|
release-linux: | $(DIST_DIRS)
|
||||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
$(GO) get -u src.techknowlogick.com/xgo; \
|
||||||
fi
|
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) .
|
CGO_CFLAGS="$(CGO_CFLAGS)" GO111MODULE=off xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/mips64le,linux/mips,linux/mipsle' -out gitea-$(VERSION) .
|
||||||
ifeq ($(CI),drone)
|
ifeq ($(CI),drone)
|
||||||
cp /build/* $(DIST)/binaries
|
cp /build/* $(DIST)/binaries
|
||||||
endif
|
endif
|
||||||
@@ -615,9 +549,9 @@ endif
|
|||||||
.PHONY: release-darwin
|
.PHONY: release-darwin
|
||||||
release-darwin: | $(DIST_DIRS)
|
release-darwin: | $(DIST_DIRS)
|
||||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
$(GO) get -u src.techknowlogick.com/xgo; \
|
||||||
fi
|
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) .
|
CGO_CFLAGS="$(CGO_CFLAGS)" GO111MODULE=off xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/*' -out gitea-$(VERSION) .
|
||||||
ifeq ($(CI),drone)
|
ifeq ($(CI),drone)
|
||||||
cp /build/* $(DIST)/binaries
|
cp /build/* $(DIST)/binaries
|
||||||
endif
|
endif
|
||||||
@@ -633,29 +567,16 @@ release-check: | $(DIST_DIRS)
|
|||||||
.PHONY: release-compress
|
.PHONY: release-compress
|
||||||
release-compress: | $(DIST_DIRS)
|
release-compress: | $(DIST_DIRS)
|
||||||
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
$(GO) install github.com/ulikunitz/xz/cmd/gxz@v0.5.10; \
|
GO111MODULE=off $(GO) get -u github.com/ulikunitz/xz/cmd/gxz; \
|
||||||
fi
|
fi
|
||||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
|
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
|
||||||
|
|
||||||
.PHONY: release-sources
|
.PHONY: release-sources
|
||||||
release-sources: | $(DIST_DIRS)
|
release-sources: | $(DIST_DIRS) node_modules
|
||||||
echo $(VERSION) > $(STORED_VERSION_FILE)
|
echo $(VERSION) > $(STORED_VERSION_FILE)
|
||||||
# bsdtar needs a ^ to prevent matching subdirectories
|
tar --exclude=./$(DIST) --exclude=./.git --exclude=./$(MAKE_EVIDENCE_DIR) --exclude=./node_modules/.cache -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz .
|
||||||
$(eval EXCL := --exclude=$(shell tar --help | grep -q bsdtar && echo "^")./)
|
|
||||||
tar $(addprefix $(EXCL),$(TAR_EXCLUDES)) -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz .
|
|
||||||
rm -f $(STORED_VERSION_FILE)
|
rm -f $(STORED_VERSION_FILE)
|
||||||
|
|
||||||
.PHONY: release-docs
|
|
||||||
release-docs: | $(DIST_DIRS) docs
|
|
||||||
tar -czf $(DIST)/release/gitea-docs-$(VERSION).tar.gz -C ./docs/public .
|
|
||||||
|
|
||||||
.PHONY: docs
|
|
||||||
docs:
|
|
||||||
@hash hugo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
curl -sL https://github.com/gohugoio/hugo/releases/download/v0.74.3/hugo_0.74.3_Linux-64bit.tar.gz | tar zxf - -C /tmp && mv /tmp/hugo /usr/bin/hugo && chmod +x /usr/bin/hugo; \
|
|
||||||
fi
|
|
||||||
cd docs; make trans-copy clean build-offline;
|
|
||||||
|
|
||||||
node_modules: package-lock.json
|
node_modules: package-lock.json
|
||||||
npm install --no-save
|
npm install --no-save
|
||||||
@touch node_modules
|
@touch node_modules
|
||||||
@@ -665,53 +586,26 @@ npm-update: node-check | node_modules
|
|||||||
npx updates -cu
|
npx updates -cu
|
||||||
rm -rf node_modules package-lock.json
|
rm -rf node_modules package-lock.json
|
||||||
npm install --package-lock
|
npm install --package-lock
|
||||||
@touch node_modules
|
|
||||||
|
|
||||||
.PHONY: fomantic
|
.PHONY: fomantic
|
||||||
fomantic:
|
fomantic: $(FOMANTIC_DEST)
|
||||||
rm -rf $(FOMANTIC_WORK_DIR)/build
|
|
||||||
cd $(FOMANTIC_WORK_DIR) && npm install --no-save
|
$(FOMANTIC_DEST): $(FOMANTIC_CONFIGS) package-lock.json | node_modules
|
||||||
cp -f $(FOMANTIC_WORK_DIR)/theme.config.less $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/theme.config
|
rm -rf $(FOMANTIC_DEST_DIR)
|
||||||
cp -rf $(FOMANTIC_WORK_DIR)/_site $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/
|
cp web_src/fomantic/theme.config.less node_modules/fomantic-ui/src/theme.config
|
||||||
cp -f web_src/js/vendor/dropdown.js $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/definitions/modules
|
cp -r web_src/fomantic/_site/* node_modules/fomantic-ui/src/_site/
|
||||||
cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build
|
cp web_src/fomantic/css.js node_modules/fomantic-ui/tasks/build/css.js
|
||||||
rm -f $(FOMANTIC_WORK_DIR)/build/*.min.*
|
npx gulp -f node_modules/fomantic-ui/gulpfile.js build
|
||||||
|
@touch $(FOMANTIC_DEST)
|
||||||
|
|
||||||
.PHONY: webpack
|
.PHONY: webpack
|
||||||
webpack: $(WEBPACK_DEST)
|
webpack: $(WEBPACK_DEST)
|
||||||
|
|
||||||
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
|
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json | node_modules
|
||||||
@$(MAKE) -s node-check node_modules
|
|
||||||
rm -rf $(WEBPACK_DEST_ENTRIES)
|
rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||||
npx webpack
|
npx webpack --hide-modules --display-entrypoints=false
|
||||||
@touch $(WEBPACK_DEST)
|
@touch $(WEBPACK_DEST)
|
||||||
|
|
||||||
.PHONY: svg
|
|
||||||
svg: node-check | node_modules
|
|
||||||
rm -rf $(SVG_DEST_DIR)
|
|
||||||
node build/generate-svg.js
|
|
||||||
|
|
||||||
.PHONY: svg-check
|
|
||||||
svg-check: svg
|
|
||||||
@git add $(SVG_DEST_DIR)
|
|
||||||
@diff=$$(git diff --cached $(SVG_DEST_DIR)); \
|
|
||||||
if [ -n "$$diff" ]; then \
|
|
||||||
echo "Please run 'make svg' and 'git add $(SVG_DEST_DIR)' and commit the result:"; \
|
|
||||||
echo "$${diff}"; \
|
|
||||||
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
|
.PHONY: update-translations
|
||||||
update-translations:
|
update-translations:
|
||||||
mkdir -p ./translations
|
mkdir -p ./translations
|
||||||
@@ -722,57 +616,48 @@ update-translations:
|
|||||||
mv ./translations/*.ini ./options/locale/
|
mv ./translations/*.ini ./options/locale/
|
||||||
rmdir ./translations
|
rmdir ./translations
|
||||||
|
|
||||||
.PHONY: generate-license
|
|
||||||
generate-license:
|
|
||||||
GO111MODULE=on $(GO) run build/generate-licenses.go
|
|
||||||
|
|
||||||
.PHONY: generate-gitignore
|
|
||||||
generate-gitignore:
|
|
||||||
GO111MODULE=on $(GO) run build/generate-gitignores.go
|
|
||||||
|
|
||||||
.PHONY: generate-images
|
.PHONY: generate-images
|
||||||
generate-images: | node_modules
|
generate-images:
|
||||||
npm install --no-save --no-package-lock fabric@4 imagemin-zopfli@7
|
$(eval TMPDIR := $(shell mktemp -d 2>/dev/null || mktemp -d -t 'gitea-temp'))
|
||||||
node build/generate-images.js $(TAGS)
|
mkdir -p $(TMPDIR)/images
|
||||||
|
inkscape -f $(PWD)/assets/logo.svg -w 880 -h 880 -e $(PWD)/public/img/gitea-lg.png
|
||||||
|
inkscape -f $(PWD)/assets/logo.svg -w 512 -h 512 -e $(PWD)/public/img/gitea-512.png
|
||||||
|
inkscape -f $(PWD)/assets/logo.svg -w 192 -h 192 -e $(PWD)/public/img/gitea-192.png
|
||||||
|
inkscape -f $(PWD)/assets/logo.svg -w 120 -h 120 -jC -i layer1 -e $(TMPDIR)/images/sm-1.png
|
||||||
|
inkscape -f $(PWD)/assets/logo.svg -w 120 -h 120 -jC -i layer2 -e $(TMPDIR)/images/sm-2.png
|
||||||
|
composite -compose atop $(TMPDIR)/images/sm-2.png $(TMPDIR)/images/sm-1.png $(PWD)/public/img/gitea-sm.png
|
||||||
|
inkscape -f $(PWD)/assets/logo.svg -w 200 -h 200 -e $(PWD)/public/img/avatar_default.png
|
||||||
|
inkscape -f $(PWD)/assets/logo.svg -w 180 -h 180 -e $(PWD)/public/img/favicon.png
|
||||||
|
inkscape -f $(PWD)/assets/logo.svg -w 128 -h 128 -e $(TMPDIR)/images/128-raw.png
|
||||||
|
inkscape -f $(PWD)/assets/logo.svg -w 64 -h 64 -e $(TMPDIR)/images/64-raw.png
|
||||||
|
inkscape -f $(PWD)/assets/logo.svg -w 32 -h 32 -jC -i layer1 -e $(TMPDIR)/images/32-1.png
|
||||||
|
inkscape -f $(PWD)/assets/logo.svg -w 32 -h 32 -jC -i layer2 -e $(TMPDIR)/images/32-2.png
|
||||||
|
composite -compose atop $(TMPDIR)/images/32-2.png $(TMPDIR)/images/32-1.png $(TMPDIR)/images/32-raw.png
|
||||||
|
inkscape -f $(PWD)/assets/logo.svg -w 16 -h 16 -jC -i layer1 -e $(TMPDIR)/images/16-raw.png
|
||||||
|
zopflipng -m -y $(TMPDIR)/images/128-raw.png $(TMPDIR)/images/128.png
|
||||||
|
zopflipng -m -y $(TMPDIR)/images/64-raw.png $(TMPDIR)/images/64.png
|
||||||
|
zopflipng -m -y $(TMPDIR)/images/32-raw.png $(TMPDIR)/images/32.png
|
||||||
|
zopflipng -m -y $(TMPDIR)/images/16-raw.png $(TMPDIR)/images/16.png
|
||||||
|
rm -f $(TMPDIR)/images/*-*.png
|
||||||
|
convert $(TMPDIR)/images/16.png $(TMPDIR)/images/32.png \
|
||||||
|
$(TMPDIR)/images/64.png $(TMPDIR)/images/128.png \
|
||||||
|
$(PWD)/public/img/favicon.ico
|
||||||
|
convert -flatten $(PWD)/public/img/favicon.png $(PWD)/public/img/apple-touch-icon.png
|
||||||
|
|
||||||
.PHONY: generate-manpage
|
rm -rf $(TMPDIR)/images
|
||||||
generate-manpage:
|
$(foreach file, $(shell find public/img -type f -name '*.png' ! -name 'loading.png'),zopflipng -m -y $(file) $(file);)
|
||||||
@[ -f gitea ] || make backend
|
|
||||||
@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 smal script witch format config-cheat-sheet.en-us.md nicely to suit as config man page
|
|
||||||
|
|
||||||
.PHONY: pr\#%
|
.PHONY: pr\#%
|
||||||
pr\#%: clean-all
|
pr\#%: clean-all
|
||||||
$(GO) run contrib/pr/checkout.go $*
|
$(GO) run contrib/pr/checkout.go $*
|
||||||
|
|
||||||
.PHONY: golangci-lint
|
.PHONY: golangci-lint
|
||||||
golangci-lint: golangci-lint-check
|
golangci-lint:
|
||||||
golangci-lint run --timeout 10m
|
|
||||||
|
|
||||||
.PHONY: golangci-lint-check
|
|
||||||
golangci-lint-check:
|
|
||||||
$(eval GOLANGCI_LINT_VERSION := $(shell printf "%03d%03d%03d" $(shell golangci-lint --version | grep -Eo '[0-9]+\.[0-9.]+' | tr '.' ' ');))
|
|
||||||
$(eval MIN_GOLANGCI_LINT_VER_FMT := $(shell printf "%g.%g.%g" $(shell echo $(MIN_GOLANGCI_LINT_VERSION) | grep -o ...)))
|
|
||||||
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
echo "Downloading golangci-lint v${MIN_GOLANGCI_LINT_VER_FMT}"; \
|
|
||||||
export BINARY="golangci-lint"; \
|
export BINARY="golangci-lint"; \
|
||||||
curl -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/v${MIN_GOLANGCI_LINT_VER_FMT}/install.sh" | sh -s -- -b $(GOPATH)/bin v$(MIN_GOLANGCI_LINT_VER_FMT); \
|
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.24.0; \
|
||||||
elif [ "$(GOLANGCI_LINT_VERSION)" -lt "$(MIN_GOLANGCI_LINT_VERSION)" ]; then \
|
|
||||||
echo "Downloading newer version of golangci-lint v${MIN_GOLANGCI_LINT_VER_FMT}"; \
|
|
||||||
export BINARY="golangci-lint"; \
|
|
||||||
curl -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/v${MIN_GOLANGCI_LINT_VER_FMT}/install.sh" | sh -s -- -b $(GOPATH)/bin v$(MIN_GOLANGCI_LINT_VER_FMT); \
|
|
||||||
fi
|
fi
|
||||||
|
golangci-lint run --timeout 5m
|
||||||
.PHONY: docker
|
|
||||||
docker:
|
|
||||||
docker build --disable-content-trust=false -t $(DOCKER_REF) .
|
|
||||||
# support also build args docker build --build-arg GITEA_VERSION=v1.2.3 --build-arg TAGS="bindata sqlite sqlite_unlock_notify" .
|
|
||||||
|
|
||||||
.PHONY: docker-build
|
|
||||||
docker-build:
|
|
||||||
docker run -ti --rm -v "$(CURDIR):/srv/app/src/code.gitea.io/gitea" -w /srv/app/src/code.gitea.io/gitea -e TAGS="bindata $(TAGS)" LDFLAGS="$(LDFLAGS)" CGO_EXTRA_CFLAGS="$(CGO_EXTRA_CFLAGS)" webhippie/golang:edge make clean build
|
|
||||||
|
|
||||||
# This endif closes the if at the top of the file
|
# This endif closes the if at the top of the file
|
||||||
endif
|
endif
|
||||||
|
|||||||
93
README.md
93
README.md
@@ -1,52 +1,18 @@
|
|||||||
<p align="center">
|
[简体中文](README_ZH.md)
|
||||||
<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">
|
<h1> <img src="https://raw.githubusercontent.com/go-gitea/gitea/master/public/img/gitea-192.png" alt="logo" width="30" height="30"> Gitea - Git with a cup of tea</h1>
|
||||||
<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://goreportcard.com/report/code.gitea.io/gitea" title="Go Report Card">
|
|
||||||
<img src="https://goreportcard.com/badge/code.gitea.io/gitea">
|
|
||||||
</a>
|
|
||||||
<a href="https://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&branch=main" title="TODOs">
|
|
||||||
<img src="https://badgen.net/https/api.tickgit.com/badgen/github.com/go-gitea/gitea/main">
|
|
||||||
</a>
|
|
||||||
<a href="https://www.bountysource.com/teams/gitea" title="Bountysource">
|
|
||||||
<img src="https://img.shields.io/bountysource/team/gitea/activity">
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p align="center">
|
[](https://drone.gitea.io/go-gitea/gitea)
|
||||||
<a href="README_ZH.md">View the chinese version of this document</a>
|
[](https://discord.gg/Gitea)
|
||||||
</p>
|
[](https://microbadger.com/images/gitea/gitea "Get your own image badge on microbadger.com")
|
||||||
|
[](https://codecov.io/gh/go-gitea/gitea)
|
||||||
|
[](https://goreportcard.com/report/code.gitea.io/gitea)
|
||||||
|
[](https://godoc.org/code.gitea.io/gitea)
|
||||||
|
[](https://github.com/go-gitea/gitea/releases/latest)
|
||||||
|
[](https://www.codetriage.com/go-gitea/gitea)
|
||||||
|
[](https://opencollective.com/gitea)
|
||||||
|
[](https://opensource.org/licenses/MIT)
|
||||||
|
[](https://crowdin.com/project/gitea)
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
@@ -67,18 +33,18 @@ From the root of the source tree, run:
|
|||||||
|
|
||||||
TAGS="bindata" make build
|
TAGS="bindata" make build
|
||||||
|
|
||||||
or if SQLite support is required:
|
or if sqlite support is required:
|
||||||
|
|
||||||
TAGS="bindata sqlite sqlite_unlock_notify" make build
|
TAGS="bindata sqlite sqlite_unlock_notify" make build
|
||||||
|
|
||||||
The `build` target is split into two sub-targets:
|
The `build` target is split into two sub-targets:
|
||||||
|
|
||||||
- `make backend` which requires [Go 1.16](https://golang.org/dl/) or greater.
|
- `make backend` which requires [Go 1.12](https://golang.org/dl/) or greater.
|
||||||
- `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater and Internet connectivity to download npm dependencies.
|
- `make frontend` which requires [Node.js 10.13](https://nodejs.org/en/download/) or greater.
|
||||||
|
|
||||||
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.
|
If pre-built frontend files are present it is possible to only build the backend:
|
||||||
|
|
||||||
Parallelism (`make -j <num>`) is not supported.
|
TAGS="bindata" make backend
|
||||||
|
|
||||||
More info: https://docs.gitea.io/en-us/install-from-source/
|
More info: https://docs.gitea.io/en-us/install-from-source/
|
||||||
|
|
||||||
@@ -98,24 +64,13 @@ NOTES:
|
|||||||
1. **YOU MUST READ THE [CONTRIBUTORS GUIDE](CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST.**
|
1. **YOU MUST READ THE [CONTRIBUTORS GUIDE](CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST.**
|
||||||
2. If you have found a vulnerability in the project, please write privately to **security@gitea.io**. Thanks!
|
2. If you have found a vulnerability in the project, please write privately to **security@gitea.io**. Thanks!
|
||||||
|
|
||||||
## Translating
|
|
||||||
|
|
||||||
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/
|
|
||||||
|
|
||||||
[](https://crowdin.com/project/gitea)
|
|
||||||
|
|
||||||
## Further information
|
## Further information
|
||||||
|
|
||||||
For more information and instructions about how to install Gitea, please look at our [documentation](https://docs.gitea.io/en-us/).
|
For more information and instructions about how to install Gitea, please look
|
||||||
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/).
|
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
|
||||||
We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea).
|
our [Discord server](https://discord.gg/Gitea),
|
||||||
The hugo-based documentation theme is hosted at [gitea/theme](https://gitea.com/gitea/theme).
|
or [forum](https://discourse.gitea.io/)!
|
||||||
The official Gitea CLI is developed at [gitea/tea](https://gitea.com/gitea/tea).
|
|
||||||
|
|
||||||
## Authors
|
## Authors
|
||||||
|
|
||||||
@@ -157,7 +112,7 @@ We're [working on it](https://github.com/go-gitea/gitea/issues/1029).
|
|||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the MIT License.
|
This project is licensed under the MIT License.
|
||||||
See the [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) file
|
See the [LICENSE](https://github.com/go-gitea/gitea/blob/master/LICENSE) file
|
||||||
for the full license text.
|
for the full license text.
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|||||||
67
README_ZH.md
67
README_ZH.md
@@ -1,52 +1,18 @@
|
|||||||
<p align="center">
|
[English](README.md)
|
||||||
<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">
|
<h1> <img src="https://raw.githubusercontent.com/go-gitea/gitea/master/public/img/gitea-192.png" alt="logo" width="30" height="30"> Gitea - Git with a cup of tea</h1>
|
||||||
<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://goreportcard.com/report/code.gitea.io/gitea" title="Go Report Card">
|
|
||||||
<img src="https://goreportcard.com/badge/code.gitea.io/gitea">
|
|
||||||
</a>
|
|
||||||
<a href="https://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&branch=main" title="TODOs">
|
|
||||||
<img src="https://badgen.net/https/api.tickgit.com/badgen/github.com/go-gitea/gitea/main">
|
|
||||||
</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">
|
[](https://drone.gitea.io/go-gitea/gitea)
|
||||||
<a href="README.md">View the english version of this document</a>
|
[](https://discord.gg/Gitea)
|
||||||
</p>
|
[](https://microbadger.com/images/gitea/gitea "Get your own image badge on microbadger.com")
|
||||||
|
[](https://codecov.io/gh/go-gitea/gitea)
|
||||||
|
[](https://goreportcard.com/report/code.gitea.io/gitea)
|
||||||
|
[](https://godoc.org/code.gitea.io/gitea)
|
||||||
|
[](https://github.com/go-gitea/gitea/releases/latest)
|
||||||
|
[](https://www.codetriage.com/go-gitea/gitea)
|
||||||
|
[](https://opencollective.com/gitea)
|
||||||
|
[](https://opensource.org/licenses/MIT)
|
||||||
|
[](https://crowdin.com/project/gitea)
|
||||||
|
|
||||||
## 目标
|
## 目标
|
||||||
|
|
||||||
@@ -68,11 +34,6 @@ Gitea 的首要目标是创建一个极易安装,运行非常快速,安装
|
|||||||
|
|
||||||
Fork -> Patch -> Push -> Pull Request
|
Fork -> Patch -> Push -> Pull Request
|
||||||
|
|
||||||
## 翻译
|
|
||||||
|
|
||||||
多语言翻译是基于Crowdin进行的.
|
|
||||||
[](https://crowdin.com/project/gitea)
|
|
||||||
|
|
||||||
## 作者
|
## 作者
|
||||||
|
|
||||||
* [Maintainers](https://github.com/orgs/go-gitea/people)
|
* [Maintainers](https://github.com/orgs/go-gitea/people)
|
||||||
@@ -81,7 +42,7 @@ Fork -> Patch -> Push -> Pull Request
|
|||||||
|
|
||||||
## 授权许可
|
## 授权许可
|
||||||
|
|
||||||
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件中。
|
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/go-gitea/gitea/blob/master/LICENSE) 文件中。
|
||||||
|
|
||||||
## 截图
|
## 截图
|
||||||
|
|
||||||
|
|||||||
10
SECURITY.md
10
SECURITY.md
@@ -1,10 +0,0 @@
|
|||||||
# 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.
|
|
||||||
189
assets/logo.svg
189
assets/logo.svg
@@ -1,31 +1,160 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg version="1.1" id="main_outline" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
y="0px" viewBox="0 0 640 640" style="enable-background:new 0 0 640 640;" xml:space="preserve">
|
|
||||||
<g>
|
<svg
|
||||||
<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
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
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
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
c2.1,6.1,2,13.1-1,19.3l-61,126.9C423.6,484.9,408.4,490.3,395.9,484.2z"/>
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
<g>
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
<g>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<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
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
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
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
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
|
width="512"
|
||||||
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
|
height="512"
|
||||||
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
|
viewBox="0 0 135.46667 135.46667"
|
||||||
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
|
version="1.1"
|
||||||
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
|
id="svg8"
|
||||||
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
|
sodipodi:docname="logo.svg"
|
||||||
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
|
inkscape:version="0.92.1 r15371"
|
||||||
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"/>
|
inkscape:export-filename=""
|
||||||
<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
|
inkscape:export-xdpi="48.000004"
|
||||||
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
|
inkscape:export-ydpi="48.000004">
|
||||||
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
|
<defs
|
||||||
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
|
id="defs2" />
|
||||||
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
|
<sodipodi:namedview
|
||||||
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
|
id="base"
|
||||||
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
|
pagecolor="#ffffff"
|
||||||
C343.2,346.5,335,363.3,326.8,380.1z"/>
|
bordercolor="#666666"
|
||||||
</g>
|
borderopacity="1.0"
|
||||||
</g>
|
inkscape:pageopacity="0"
|
||||||
</g>
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="0.70710678"
|
||||||
|
inkscape:cx="418.13805"
|
||||||
|
inkscape:cy="177.57445"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer2"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
width="256px"
|
||||||
|
showguides="false"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1137"
|
||||||
|
inkscape:window-x="1912"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:pagecheckerboard="false"
|
||||||
|
inkscape:measure-start="283.373,243.952"
|
||||||
|
inkscape:measure-end="290.267,236.527">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="0,0"
|
||||||
|
orientation="0,512"
|
||||||
|
id="guide3699"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="135.46667,0"
|
||||||
|
orientation="-512,0"
|
||||||
|
id="guide3701"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="135.46667,135.46667"
|
||||||
|
orientation="0,-512"
|
||||||
|
id="guide3703"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="0,135.46667"
|
||||||
|
orientation="512,0"
|
||||||
|
id="guide3705"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-161.53334)"
|
||||||
|
style="display:inline">
|
||||||
|
<path
|
||||||
|
style="fill:#609926;fill-opacity:1;stroke:#428f29;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
d="m 27.709937,195.15095 c -9.546573,-0.0272 -22.3392732,6.79805 -21.6317552,23.90397 1.105534,26.72889 25.4565952,29.20839 35.1916502,29.42301 1.068023,5.01357 12.521798,22.30563 21.001818,23.21667 h 37.15277 c 22.27763,-1.66785 38.9607,-75.75671 26.59321,-76.03825 -46.781583,2.47691 -49.995146,2.13838 -88.599758,0 -2.495053,-0.0266 -5.972321,-0.49474 -9.707935,-0.5054 z m 2.491319,9.45886 c 1.351378,13.69267 3.555849,21.70359 8.018216,33.94345 -11.382872,-1.50473 -21.069822,-5.22443 -22.851515,-19.10984 -0.950962,-7.4112 2.390428,-15.16769 14.833299,-14.83361 z"
|
||||||
|
id="path3722"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="sscccccsccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer2"
|
||||||
|
inkscape:label="Layer 2"
|
||||||
|
style="display:inline">
|
||||||
|
<rect
|
||||||
|
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.24757317;stroke-opacity:1"
|
||||||
|
id="rect4599"
|
||||||
|
width="34.762054"
|
||||||
|
height="34.762054"
|
||||||
|
x="87.508659"
|
||||||
|
y="18.291576"
|
||||||
|
transform="rotate(25.914715)"
|
||||||
|
ry="5.4825778" />
|
||||||
|
<path
|
||||||
|
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26644793px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 79.804947,57.359056 3.241146,1.609954 V 35.255731 h -3.262698 z"
|
||||||
|
id="path4525"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer3"
|
||||||
|
inkscape:label="Layer 3"
|
||||||
|
style="display:inline">
|
||||||
|
<g
|
||||||
|
style="display:inline"
|
||||||
|
id="g4539">
|
||||||
|
<circle
|
||||||
|
transform="rotate(-19.796137)"
|
||||||
|
r="3.4745038"
|
||||||
|
cy="90.077766"
|
||||||
|
cx="49.064713"
|
||||||
|
id="path4606"
|
||||||
|
style="fill:#609926;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" />
|
||||||
|
<circle
|
||||||
|
transform="rotate(-19.796137)"
|
||||||
|
r="3.4745038"
|
||||||
|
cy="102.1049"
|
||||||
|
cx="36.810425"
|
||||||
|
id="path4606-3"
|
||||||
|
style="fill:#609926;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" />
|
||||||
|
<circle
|
||||||
|
transform="rotate(-19.796137)"
|
||||||
|
r="3.4745038"
|
||||||
|
cy="111.43928"
|
||||||
|
cx="46.484283"
|
||||||
|
id="path4606-1"
|
||||||
|
style="fill:#609926;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" />
|
||||||
|
<rect
|
||||||
|
transform="rotate(26.024158)"
|
||||||
|
y="18.061695"
|
||||||
|
x="97.333458"
|
||||||
|
height="27.261492"
|
||||||
|
width="2.6726954"
|
||||||
|
id="rect4629-8"
|
||||||
|
style="fill:#609926;fill-opacity:1;stroke:none;stroke-width:0.27444693;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
sodipodi:nodetypes="cc"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4514"
|
||||||
|
d="m 76.558096,68.116343 c 12.97589,6.395378 13.012989,4.101862 4.890858,20.907244"
|
||||||
|
style="fill:none;stroke:#609926;stroke-width:2.68000007;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 5.6 KiB |
13
build.go
13
build.go
@@ -2,8 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build vendor
|
//+build vendor
|
||||||
// +build vendor
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -11,6 +10,14 @@ package main
|
|||||||
// These libraries will not be included in a normal compilation.
|
// These libraries will not be included in a normal compilation.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
// for lint
|
||||||
|
_ "github.com/BurntSushi/toml"
|
||||||
|
_ "github.com/mgechev/dots"
|
||||||
|
_ "github.com/mgechev/revive/formatter"
|
||||||
|
_ "github.com/mgechev/revive/lint"
|
||||||
|
_ "github.com/mgechev/revive/rule"
|
||||||
|
_ "github.com/mitchellh/go-homedir"
|
||||||
|
|
||||||
// for embed
|
// for embed
|
||||||
_ "github.com/shurcooL/vfsgen"
|
_ "github.com/shurcooL/vfsgen"
|
||||||
|
|
||||||
@@ -18,7 +25,7 @@ import (
|
|||||||
_ "golang.org/x/tools/cover"
|
_ "golang.org/x/tools/cover"
|
||||||
|
|
||||||
// for vet
|
// for vet
|
||||||
_ "code.gitea.io/gitea-vet"
|
_ "gitea.com/jolheiser/gitea-vet"
|
||||||
|
|
||||||
// for swagger
|
// for swagger
|
||||||
_ "github.com/go-swagger/go-swagger/cmd/swagger"
|
_ "github.com/go-swagger/go-swagger/cmd/swagger"
|
||||||
|
|||||||
284
build/code-batch-process.go
vendored
284
build/code-batch-process.go
vendored
@@ -1,284 +0,0 @@
|
|||||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build ignore
|
|
||||||
// +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/misspell) 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: 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",
|
|
||||||
"integrations",
|
|
||||||
"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(`integrations/gitea-repositories-meta`))
|
|
||||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`integrations/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 []string, 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 ...
|
|
||||||
%[1]s misspell ...
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
{file-list} the file list
|
|
||||||
|
|
||||||
Example:
|
|
||||||
%[1]s gofmt -s -d {file-list}
|
|
||||||
|
|
||||||
`, "file-batch-exec")
|
|
||||||
}
|
|
||||||
|
|
||||||
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) error {
|
|
||||||
for _, file := range files {
|
|
||||||
if err := codeformat.FormatGoImports(file); 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, "-w") {
|
|
||||||
cmdErrors = append(cmdErrors, giteaFormatGoImports(files))
|
|
||||||
}
|
|
||||||
cmdErrors = append(cmdErrors, passThroughCmd("gofmt", substArgs))
|
|
||||||
case "misspell":
|
|
||||||
cmdErrors = append(cmdErrors, passThroughCmd("misspell", substArgs))
|
|
||||||
default:
|
|
||||||
log.Fatalf("unknown cmd: %s %v", subCmd, subArgs)
|
|
||||||
}
|
|
||||||
processed += len(files)
|
|
||||||
}
|
|
||||||
|
|
||||||
logVerbose("processed %d files", processed)
|
|
||||||
exitWithCmdErrors(subCmd, subArgs, cmdErrors)
|
|
||||||
}
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
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")
|
|
||||||
var 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
|
|
||||||
type 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) 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
|
|
||||||
}
|
|
||||||
f, err = os.OpenFile(file, os.O_TRUNC|os.O_WRONLY, 0644)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
_, err = f.Write(formattedBytes)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
21
build/generate-bindata.go
vendored
21
build/generate-bindata.go
vendored
@@ -2,7 +2,6 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build ignore
|
|
||||||
// +build ignore
|
// +build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
@@ -11,6 +10,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -27,7 +27,7 @@ func needsUpdate(dir string, filename string) (bool, []byte) {
|
|||||||
needRegen = true
|
needRegen = true
|
||||||
}
|
}
|
||||||
|
|
||||||
oldHash, err := os.ReadFile(filename + ".hash")
|
oldHash, err := ioutil.ReadFile(filename + ".hash")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
oldHash = []byte{}
|
oldHash = []byte{}
|
||||||
}
|
}
|
||||||
@@ -58,15 +58,11 @@ func needsUpdate(dir string, filename string) (bool, []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) < 4 {
|
if len(os.Args) != 4 {
|
||||||
log.Fatal("Insufficient number of arguments. Need: directory packageName filename")
|
log.Fatal("Insufficient number of arguments. Need: directory packageName filename")
|
||||||
}
|
}
|
||||||
|
|
||||||
dir, packageName, filename := os.Args[1], os.Args[2], os.Args[3]
|
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)
|
update, newHash := needsUpdate(dir, filename)
|
||||||
|
|
||||||
@@ -78,14 +74,13 @@ func main() {
|
|||||||
fmt.Printf("generating bindata for %s\n", packageName)
|
fmt.Printf("generating bindata for %s\n", packageName)
|
||||||
var fsTemplates http.FileSystem = http.Dir(dir)
|
var fsTemplates http.FileSystem = http.Dir(dir)
|
||||||
err := vfsgen.Generate(fsTemplates, vfsgen.Options{
|
err := vfsgen.Generate(fsTemplates, vfsgen.Options{
|
||||||
PackageName: packageName,
|
PackageName: packageName,
|
||||||
BuildTags: "bindata",
|
BuildTags: "bindata",
|
||||||
VariableName: "Assets",
|
VariableName: "Assets",
|
||||||
Filename: filename,
|
Filename: filename,
|
||||||
UseGlobalModTime: useGlobalModTime,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("%v\n", err)
|
log.Fatalf("%v\n", err)
|
||||||
}
|
}
|
||||||
_ = os.WriteFile(filename+".hash", newHash, 0666)
|
_ = ioutil.WriteFile(filename+".hash", newHash, 0666)
|
||||||
}
|
}
|
||||||
|
|||||||
15
build/generate-emoji.go
vendored
15
build/generate-emoji.go
vendored
@@ -3,26 +3,23 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build ignore
|
|
||||||
// +build ignore
|
// +build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/format"
|
"go/format"
|
||||||
"io"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/json"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -68,7 +65,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// write
|
// write
|
||||||
err = os.WriteFile(*flagOut, buf, 0644)
|
err = ioutil.WriteFile(*flagOut, buf, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -97,7 +94,7 @@ func generate() ([]byte, error) {
|
|||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
|
|
||||||
// read all
|
// read all
|
||||||
body, err := io.ReadAll(res.Body)
|
body, err := ioutil.ReadAll(res.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -158,7 +155,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)
|
// 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)
|
file, _ := json.Marshal(data)
|
||||||
_ = os.WriteFile("assets/emoji.json", file, 0644)
|
_ = ioutil.WriteFile("assets/emoji.json", file, 0644)
|
||||||
|
|
||||||
// Add skin tones to emoji that support it
|
// Add skin tones to emoji that support it
|
||||||
var (
|
var (
|
||||||
@@ -177,7 +174,7 @@ func generate() ([]byte, error) {
|
|||||||
s = append(s, k)
|
s = append(s, k)
|
||||||
} else {
|
} else {
|
||||||
// insert into slice after first element because all emoji that support skin tones
|
// insert into slice after first element because all emoji that support skin tones
|
||||||
// have that modifier placed at this spot
|
// have that modifer placed at this spot
|
||||||
s = append(s, "")
|
s = append(s, "")
|
||||||
copy(s[2:], s[1:])
|
copy(s[2:], s[1:])
|
||||||
s[1] = k
|
s[1] = k
|
||||||
|
|||||||
33
build/generate-gitignores.go
vendored
33
build/generate-gitignores.go
vendored
@@ -1,4 +1,3 @@
|
|||||||
//go:build ignore
|
|
||||||
// +build ignore
|
// +build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
@@ -9,51 +8,39 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var (
|
var (
|
||||||
prefix = "gitea-gitignore"
|
prefix = "gitea-gitignore"
|
||||||
url = "https://api.github.com/repos/github/gitignore/tarball"
|
url = "https://api.github.com/repos/github/gitignore/tarball"
|
||||||
githubApiToken = ""
|
destination = ""
|
||||||
githubUsername = ""
|
|
||||||
destination = ""
|
|
||||||
)
|
)
|
||||||
|
|
||||||
flag.StringVar(&destination, "dest", "options/gitignore/", "destination for the gitignores")
|
flag.StringVar(&destination, "dest", "options/gitignore/", "destination for the gitignores")
|
||||||
flag.StringVar(&githubUsername, "username", "", "github username")
|
|
||||||
flag.StringVar(&githubApiToken, "token", "", "github api token")
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
file, err := os.CreateTemp(os.TempDir(), prefix)
|
file, err := ioutil.TempFile(os.TempDir(), prefix)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to create temp file. %s", err)
|
log.Fatalf("Failed to create temp file. %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer util.Remove(file.Name())
|
defer os.Remove(file.Name())
|
||||||
|
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to download archive. %s", err)
|
log.Fatalf("Failed to download archive. %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(githubApiToken) > 0 && len(githubUsername) > 0 {
|
|
||||||
req.SetBasicAuth(githubUsername, githubApiToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to download archive. %s", err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if _, err := io.Copy(file, resp.Body); err != nil {
|
if _, err := io.Copy(file, resp.Body); err != nil {
|
||||||
@@ -113,13 +100,13 @@ func main() {
|
|||||||
for dst, src := range filesToCopy {
|
for dst, src := range filesToCopy {
|
||||||
// Read all content of src to data
|
// Read all content of src to data
|
||||||
src = path.Join(destination, src)
|
src = path.Join(destination, src)
|
||||||
data, err := os.ReadFile(src)
|
data, err := ioutil.ReadFile(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to read src file. %s", err)
|
log.Fatalf("Failed to read src file. %s", err)
|
||||||
}
|
}
|
||||||
// Write data to dst
|
// Write data to dst
|
||||||
dst = path.Join(destination, dst)
|
dst = path.Join(destination, dst)
|
||||||
err = os.WriteFile(dst, data, 0644)
|
err = ioutil.WriteFile(dst, data, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to write new file. %s", err)
|
log.Fatalf("Failed to write new file. %s", err)
|
||||||
}
|
}
|
||||||
|
|||||||
84
build/generate-images.js
vendored
84
build/generate-images.js
vendored
@@ -1,84 +0,0 @@
|
|||||||
import imageminZopfli from 'imagemin-zopfli';
|
|
||||||
import {optimize} from 'svgo';
|
|
||||||
import {fabric} from 'fabric';
|
|
||||||
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);
|
|
||||||
process.exit(err ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadSvg(svg) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
fabric.loadSVGFromString(svg, (objects, options) => {
|
|
||||||
resolve({objects, options});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function generate(svg, outputFile, {size, bg}) {
|
|
||||||
if (outputFile.endsWith('.svg')) {
|
|
||||||
const {data} = optimize(svg, {
|
|
||||||
plugins: [
|
|
||||||
'preset-default',
|
|
||||||
'removeDimensions',
|
|
||||||
{
|
|
||||||
name: 'addAttributesToSVGElement',
|
|
||||||
params: {attributes: [{width: size}, {height: size}]}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
await writeFile(outputFile, data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {objects, options} = await loadSvg(svg);
|
|
||||||
const canvas = new fabric.Canvas();
|
|
||||||
canvas.setDimensions({width: size, height: size});
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
ctx.scale(options.width ? (size / options.width) : 1, options.height ? (size / options.height) : 1);
|
|
||||||
|
|
||||||
if (bg) {
|
|
||||||
canvas.add(new fabric.Rect({
|
|
||||||
left: 0,
|
|
||||||
top: 0,
|
|
||||||
height: size * (1 / (size / options.height)),
|
|
||||||
width: size * (1 / (size / options.width)),
|
|
||||||
fill: 'white',
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas.add(fabric.util.groupSVGElements(objects, options));
|
|
||||||
canvas.renderAll();
|
|
||||||
|
|
||||||
let png = Buffer.from([]);
|
|
||||||
for await (const chunk of canvas.createPNGStream()) {
|
|
||||||
png = Buffer.concat([png, chunk]);
|
|
||||||
}
|
|
||||||
|
|
||||||
png = await imageminZopfli({more: true})(png);
|
|
||||||
await writeFile(outputFile, png);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const gitea = process.argv.slice(2).includes('gitea');
|
|
||||||
const svg = await readFile(logoFile, 'utf8');
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
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);
|
|
||||||
|
|
||||||
28
build/generate-licenses.go
vendored
28
build/generate-licenses.go
vendored
@@ -1,4 +1,3 @@
|
|||||||
//go:build ignore
|
|
||||||
// +build ignore
|
// +build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
@@ -9,48 +8,35 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var (
|
var (
|
||||||
prefix = "gitea-licenses"
|
prefix = "gitea-licenses"
|
||||||
url = "https://api.github.com/repos/spdx/license-list-data/tarball"
|
url = "https://api.github.com/repos/spdx/license-list-data/tarball"
|
||||||
githubApiToken = ""
|
destination = ""
|
||||||
githubUsername = ""
|
|
||||||
destination = ""
|
|
||||||
)
|
)
|
||||||
|
|
||||||
flag.StringVar(&destination, "dest", "options/license/", "destination for the licenses")
|
flag.StringVar(&destination, "dest", "options/license/", "destination for the licenses")
|
||||||
flag.StringVar(&githubUsername, "username", "", "github username")
|
|
||||||
flag.StringVar(&githubApiToken, "token", "", "github api token")
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
file, err := os.CreateTemp(os.TempDir(), prefix)
|
file, err := ioutil.TempFile(os.TempDir(), prefix)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to create temp file. %s", err)
|
log.Fatalf("Failed to create temp file. %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer util.Remove(file.Name())
|
defer os.Remove(file.Name())
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
resp, err := http.Get(url)
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to download archive. %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(githubApiToken) > 0 && len(githubUsername) > 0 {
|
|
||||||
req.SetBasicAuth(githubUsername, githubApiToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to download archive. %s", err)
|
log.Fatalf("Failed to download archive. %s", err)
|
||||||
}
|
}
|
||||||
|
|||||||
58
build/generate-svg.js
vendored
58
build/generate-svg.js
vendored
@@ -1,58 +0,0 @@
|
|||||||
import fastGlob from 'fast-glob';
|
|
||||||
import {optimize} from 'svgo';
|
|
||||||
import {resolve, parse, dirname} from 'path';
|
|
||||||
import fs from 'fs';
|
|
||||||
import {fileURLToPath} from 'url';
|
|
||||||
|
|
||||||
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);
|
|
||||||
process.exit(err ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function processFile(file, {prefix, fullName} = {}) {
|
|
||||||
let name;
|
|
||||||
|
|
||||||
if (fullName) {
|
|
||||||
name = fullName;
|
|
||||||
} else {
|
|
||||||
name = parse(file).name;
|
|
||||||
if (prefix) name = `${prefix}-${name}`;
|
|
||||||
if (prefix === 'octicon') name = name.replace(/-[0-9]+$/, ''); // chop of '-16' on octicons
|
|
||||||
}
|
|
||||||
|
|
||||||
const {data} = optimize(await readFile(file, 'utf8'), {
|
|
||||||
plugins: [
|
|
||||||
{name: 'preset-default'},
|
|
||||||
{name: 'removeXMLNS'},
|
|
||||||
{name: 'removeDimensions'},
|
|
||||||
{name: 'prefixIds', params: {prefix: () => name}},
|
|
||||||
{name: 'addClassesToSVGElement', params: {classNames: ['svg', name]}},
|
|
||||||
{name: 'addAttributesToSVGElement', params: {attributes: [{'width': '16'}, {'height': '16'}, {'aria-hidden': 'true'}]}},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
await writeFile(resolve(outputDir, `${name}.svg`), data);
|
|
||||||
}
|
|
||||||
|
|
||||||
function processFiles(pattern, opts) {
|
|
||||||
return glob(pattern).map((file) => processFile(file, opts));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
try {
|
|
||||||
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'}),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
main().then(exit).catch(exit);
|
|
||||||
|
|
||||||
27
build/gitea-format-imports.go
vendored
27
build/gitea-format-imports.go
vendored
@@ -1,27 +0,0 @@
|
|||||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build ignore
|
|
||||||
// +build ignore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/build/codeformat"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
if len(os.Args) <= 1 {
|
|
||||||
log.Fatalf("Usage: gitea-format-imports [files...]")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, file := range os.Args[1:] {
|
|
||||||
if err := codeformat.FormatGoImports(file); err != nil {
|
|
||||||
log.Fatalf("can not format file %s, err=%v", file, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
3
build/gocovmerge.go
vendored
3
build/gocovmerge.go
vendored
@@ -6,7 +6,6 @@
|
|||||||
// gocovmerge takes the results from multiple `go test -coverprofile` runs and
|
// gocovmerge takes the results from multiple `go test -coverprofile` runs and
|
||||||
// merges them into one profile
|
// merges them into one profile
|
||||||
|
|
||||||
//go:build ignore
|
|
||||||
// +build ignore
|
// +build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
@@ -109,7 +108,7 @@ func main() {
|
|||||||
for _, file := range flag.Args() {
|
for _, file := range flag.Args() {
|
||||||
profiles, err := cover.ParseProfiles(file)
|
profiles, err := cover.ParseProfiles(file)
|
||||||
if err != nil {
|
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 {
|
for _, p := range profiles {
|
||||||
merged = addProfile(merged, p)
|
merged = addProfile(merged, p)
|
||||||
|
|||||||
325
build/lint.go
vendored
Normal file
325
build/lint.go
vendored
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Copyright (c) 2018 Minko Gechev. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/toml"
|
||||||
|
"github.com/mgechev/dots"
|
||||||
|
"github.com/mgechev/revive/formatter"
|
||||||
|
"github.com/mgechev/revive/lint"
|
||||||
|
"github.com/mgechev/revive/rule"
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
)
|
||||||
|
|
||||||
|
func fail(err string) {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultRules = []lint.Rule{
|
||||||
|
&rule.VarDeclarationsRule{},
|
||||||
|
&rule.PackageCommentsRule{},
|
||||||
|
&rule.DotImportsRule{},
|
||||||
|
&rule.BlankImportsRule{},
|
||||||
|
&rule.ExportedRule{},
|
||||||
|
&rule.VarNamingRule{},
|
||||||
|
&rule.IndentErrorFlowRule{},
|
||||||
|
&rule.IfReturnRule{},
|
||||||
|
&rule.RangeRule{},
|
||||||
|
&rule.ErrorfRule{},
|
||||||
|
&rule.ErrorNamingRule{},
|
||||||
|
&rule.ErrorStringsRule{},
|
||||||
|
&rule.ReceiverNamingRule{},
|
||||||
|
&rule.IncrementDecrementRule{},
|
||||||
|
&rule.ErrorReturnRule{},
|
||||||
|
&rule.UnexportedReturnRule{},
|
||||||
|
&rule.TimeNamingRule{},
|
||||||
|
&rule.ContextKeysType{},
|
||||||
|
&rule.ContextAsArgumentRule{},
|
||||||
|
}
|
||||||
|
|
||||||
|
var allRules = append([]lint.Rule{
|
||||||
|
&rule.ArgumentsLimitRule{},
|
||||||
|
&rule.CyclomaticRule{},
|
||||||
|
&rule.FileHeaderRule{},
|
||||||
|
&rule.EmptyBlockRule{},
|
||||||
|
&rule.SuperfluousElseRule{},
|
||||||
|
&rule.ConfusingNamingRule{},
|
||||||
|
&rule.GetReturnRule{},
|
||||||
|
&rule.ModifiesParamRule{},
|
||||||
|
&rule.ConfusingResultsRule{},
|
||||||
|
&rule.DeepExitRule{},
|
||||||
|
&rule.UnusedParamRule{},
|
||||||
|
&rule.UnreachableCodeRule{},
|
||||||
|
&rule.AddConstantRule{},
|
||||||
|
&rule.FlagParamRule{},
|
||||||
|
&rule.UnnecessaryStmtRule{},
|
||||||
|
&rule.StructTagRule{},
|
||||||
|
&rule.ModifiesValRecRule{},
|
||||||
|
&rule.ConstantLogicalExprRule{},
|
||||||
|
&rule.BoolLiteralRule{},
|
||||||
|
&rule.RedefinesBuiltinIDRule{},
|
||||||
|
&rule.ImportsBlacklistRule{},
|
||||||
|
&rule.FunctionResultsLimitRule{},
|
||||||
|
&rule.MaxPublicStructsRule{},
|
||||||
|
&rule.RangeValInClosureRule{},
|
||||||
|
&rule.RangeValAddress{},
|
||||||
|
&rule.WaitGroupByValueRule{},
|
||||||
|
&rule.AtomicRule{},
|
||||||
|
&rule.EmptyLinesRule{},
|
||||||
|
&rule.LineLengthLimitRule{},
|
||||||
|
&rule.CallToGCRule{},
|
||||||
|
&rule.DuplicatedImportsRule{},
|
||||||
|
&rule.ImportShadowingRule{},
|
||||||
|
&rule.BareReturnRule{},
|
||||||
|
&rule.UnusedReceiverRule{},
|
||||||
|
&rule.UnhandledErrorRule{},
|
||||||
|
&rule.CognitiveComplexityRule{},
|
||||||
|
&rule.StringOfIntRule{},
|
||||||
|
}, defaultRules...)
|
||||||
|
|
||||||
|
var allFormatters = []lint.Formatter{
|
||||||
|
&formatter.Stylish{},
|
||||||
|
&formatter.Friendly{},
|
||||||
|
&formatter.JSON{},
|
||||||
|
&formatter.NDJSON{},
|
||||||
|
&formatter.Default{},
|
||||||
|
&formatter.Unix{},
|
||||||
|
&formatter.Checkstyle{},
|
||||||
|
&formatter.Plain{},
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFormatters() map[string]lint.Formatter {
|
||||||
|
result := map[string]lint.Formatter{}
|
||||||
|
for _, f := range allFormatters {
|
||||||
|
result[f.Name()] = f
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLintingRules(config *lint.Config) []lint.Rule {
|
||||||
|
rulesMap := map[string]lint.Rule{}
|
||||||
|
for _, r := range allRules {
|
||||||
|
rulesMap[r.Name()] = r
|
||||||
|
}
|
||||||
|
|
||||||
|
lintingRules := []lint.Rule{}
|
||||||
|
for name := range config.Rules {
|
||||||
|
rule, ok := rulesMap[name]
|
||||||
|
if !ok {
|
||||||
|
fail("cannot find rule: " + name)
|
||||||
|
}
|
||||||
|
lintingRules = append(lintingRules, rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lintingRules
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseConfig(path string) *lint.Config {
|
||||||
|
config := &lint.Config{}
|
||||||
|
file, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
fail("cannot read the config file")
|
||||||
|
}
|
||||||
|
_, err = toml.Decode(string(file), config)
|
||||||
|
if err != nil {
|
||||||
|
fail("cannot parse the config file: " + err.Error())
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeConfig(config *lint.Config) {
|
||||||
|
if config.Confidence == 0 {
|
||||||
|
config.Confidence = 0.8
|
||||||
|
}
|
||||||
|
severity := config.Severity
|
||||||
|
if severity != "" {
|
||||||
|
for k, v := range config.Rules {
|
||||||
|
if v.Severity == "" {
|
||||||
|
v.Severity = severity
|
||||||
|
}
|
||||||
|
config.Rules[k] = v
|
||||||
|
}
|
||||||
|
for k, v := range config.Directives {
|
||||||
|
if v.Severity == "" {
|
||||||
|
v.Severity = severity
|
||||||
|
}
|
||||||
|
config.Directives[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConfig() *lint.Config {
|
||||||
|
config := defaultConfig()
|
||||||
|
if configPath != "" {
|
||||||
|
config = parseConfig(configPath)
|
||||||
|
}
|
||||||
|
normalizeConfig(config)
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFormatter() lint.Formatter {
|
||||||
|
formatters := getFormatters()
|
||||||
|
formatter := formatters["default"]
|
||||||
|
if formatterName != "" {
|
||||||
|
f, ok := formatters[formatterName]
|
||||||
|
if !ok {
|
||||||
|
fail("unknown formatter " + formatterName)
|
||||||
|
}
|
||||||
|
formatter = f
|
||||||
|
}
|
||||||
|
return formatter
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildDefaultConfigPath() string {
|
||||||
|
var result string
|
||||||
|
if homeDir, err := homedir.Dir(); err == nil {
|
||||||
|
result = filepath.Join(homeDir, "revive.toml")
|
||||||
|
if _, err := os.Stat(result); err != nil {
|
||||||
|
result = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultConfig() *lint.Config {
|
||||||
|
defaultConfig := lint.Config{
|
||||||
|
Confidence: 0.0,
|
||||||
|
Severity: lint.SeverityWarning,
|
||||||
|
Rules: map[string]lint.RuleConfig{},
|
||||||
|
}
|
||||||
|
for _, r := range defaultRules {
|
||||||
|
defaultConfig.Rules[r.Name()] = lint.RuleConfig{}
|
||||||
|
}
|
||||||
|
return &defaultConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeSplit(strs []string) []string {
|
||||||
|
res := []string{}
|
||||||
|
for _, s := range strs {
|
||||||
|
t := strings.Trim(s, " \t")
|
||||||
|
if len(t) > 0 {
|
||||||
|
res = append(res, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPackages() [][]string {
|
||||||
|
globs := normalizeSplit(flag.Args())
|
||||||
|
if len(globs) == 0 {
|
||||||
|
globs = append(globs, ".")
|
||||||
|
}
|
||||||
|
|
||||||
|
packages, err := dots.ResolvePackages(globs, normalizeSplit(excludePaths))
|
||||||
|
if err != nil {
|
||||||
|
fail(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return packages
|
||||||
|
}
|
||||||
|
|
||||||
|
type arrayFlags []string
|
||||||
|
|
||||||
|
func (i *arrayFlags) String() string {
|
||||||
|
return strings.Join([]string(*i), " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *arrayFlags) Set(value string) error {
|
||||||
|
*i = append(*i, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var configPath string
|
||||||
|
var excludePaths arrayFlags
|
||||||
|
var formatterName string
|
||||||
|
var help bool
|
||||||
|
|
||||||
|
var originalUsage = flag.Usage
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flag.Usage = func() {
|
||||||
|
originalUsage()
|
||||||
|
}
|
||||||
|
// command line help strings
|
||||||
|
const (
|
||||||
|
configUsage = "path to the configuration TOML file, defaults to $HOME/revive.toml, if present (i.e. -config myconf.toml)"
|
||||||
|
excludeUsage = "list of globs which specify files to be excluded (i.e. -exclude foo/...)"
|
||||||
|
formatterUsage = "formatter to be used for the output (i.e. -formatter stylish)"
|
||||||
|
)
|
||||||
|
|
||||||
|
defaultConfigPath := buildDefaultConfigPath()
|
||||||
|
|
||||||
|
flag.StringVar(&configPath, "config", defaultConfigPath, configUsage)
|
||||||
|
flag.Var(&excludePaths, "exclude", excludeUsage)
|
||||||
|
flag.StringVar(&formatterName, "formatter", "", formatterUsage)
|
||||||
|
flag.Parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
config := getConfig()
|
||||||
|
formatter := getFormatter()
|
||||||
|
packages := getPackages()
|
||||||
|
|
||||||
|
revive := lint.New(func(file string) ([]byte, error) {
|
||||||
|
return ioutil.ReadFile(file)
|
||||||
|
})
|
||||||
|
|
||||||
|
lintingRules := getLintingRules(config)
|
||||||
|
|
||||||
|
failures, err := revive.Lint(packages, lintingRules, *config)
|
||||||
|
if err != nil {
|
||||||
|
fail(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
formatChan := make(chan lint.Failure)
|
||||||
|
exitChan := make(chan bool)
|
||||||
|
|
||||||
|
var output string
|
||||||
|
go (func() {
|
||||||
|
output, err = formatter.Format(formatChan, *config)
|
||||||
|
if err != nil {
|
||||||
|
fail(err.Error())
|
||||||
|
}
|
||||||
|
exitChan <- true
|
||||||
|
})()
|
||||||
|
|
||||||
|
exitCode := 0
|
||||||
|
for f := range failures {
|
||||||
|
if f.Confidence < config.Confidence {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if exitCode == 0 {
|
||||||
|
exitCode = config.WarningCode
|
||||||
|
}
|
||||||
|
if c, ok := config.Rules[f.RuleName]; ok && c.Severity == lint.SeverityError {
|
||||||
|
exitCode = config.ErrorCode
|
||||||
|
}
|
||||||
|
if c, ok := config.Directives[f.RuleName]; ok && c.Severity == lint.SeverityError {
|
||||||
|
exitCode = config.ErrorCode
|
||||||
|
}
|
||||||
|
|
||||||
|
formatChan <- f
|
||||||
|
}
|
||||||
|
|
||||||
|
close(formatChan)
|
||||||
|
<-exitChan
|
||||||
|
if output != "" {
|
||||||
|
fmt.Println(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(exitCode)
|
||||||
|
}
|
||||||
24
build/test-env-check.sh
vendored
24
build/test-env-check.sh
vendored
@@ -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
|
|
||||||
11
build/test-env-prepare.sh
vendored
11
build/test-env-prepare.sh
vendored
@@ -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 .
|
|
||||||
4
build/update-locales.sh
vendored
4
build/update-locales.sh
vendored
@@ -10,10 +10,10 @@ sed -i -r -e '/^[a-zA-Z0-9_.-]+[ ]*=[ ]*".*"$/ {
|
|||||||
}' ./options/locale/*.ini
|
}' ./options/locale/*.ini
|
||||||
|
|
||||||
# Remove translation under 25% of en_us
|
# Remove translation under 25% of en_us
|
||||||
baselines=$(wc -l "./options/locale_en-US.ini" | cut -d" " -f1)
|
baselines=`wc -l "./options/locale_en-US.ini" | cut -d" " -f1`
|
||||||
baselines=$((baselines / 4))
|
baselines=$((baselines / 4))
|
||||||
for filename in ./options/locale/*.ini; do
|
for filename in ./options/locale/*.ini; do
|
||||||
lines=$(wc -l "$filename" | cut -d" " -f1)
|
lines=`wc -l "$filename" | cut -d" " -f1`
|
||||||
if [ $lines -lt $baselines ]; then
|
if [ $lines -lt $baselines ]; then
|
||||||
echo "Removing $filename: $lines/$baselines"
|
echo "Removing $filename: $lines/$baselines"
|
||||||
rm "$filename"
|
rm "$filename"
|
||||||
|
|||||||
527
cmd/admin.go
527
cmd/admin.go
@@ -6,30 +6,19 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
"code.gitea.io/gitea/modules/auth/oauth2"
|
||||||
"code.gitea.io/gitea/models/auth"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/graceful"
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
pwd "code.gitea.io/gitea/modules/password"
|
pwd "code.gitea.io/gitea/modules/password"
|
||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/storage"
|
|
||||||
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"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
@@ -40,39 +29,16 @@ var (
|
|||||||
Name: "admin",
|
Name: "admin",
|
||||||
Usage: "Command line interface to perform common administrative operations",
|
Usage: "Command line interface to perform common administrative operations",
|
||||||
Subcommands: []cli.Command{
|
Subcommands: []cli.Command{
|
||||||
subcmdUser,
|
subcmdCreateUser,
|
||||||
|
subcmdChangePassword,
|
||||||
subcmdRepoSyncReleases,
|
subcmdRepoSyncReleases,
|
||||||
subcmdRegenerate,
|
subcmdRegenerate,
|
||||||
subcmdAuth,
|
subcmdAuth,
|
||||||
subcmdSendMail,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
subcmdUser = cli.Command{
|
subcmdCreateUser = cli.Command{
|
||||||
Name: "user",
|
Name: "create-user",
|
||||||
Usage: "Modify users",
|
|
||||||
Subcommands: []cli.Command{
|
|
||||||
microcmdUserCreate,
|
|
||||||
microcmdUserList,
|
|
||||||
microcmdUserChangePassword,
|
|
||||||
microcmdUserDelete,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdUserList = cli.Command{
|
|
||||||
Name: "list",
|
|
||||||
Usage: "List users",
|
|
||||||
Action: runListUsers,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "admin",
|
|
||||||
Usage: "List only admin users",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdUserCreate = cli.Command{
|
|
||||||
Name: "create",
|
|
||||||
Usage: "Create a new user in database",
|
Usage: "Create a new user in database",
|
||||||
Action: runCreateUser,
|
Action: runCreateUser,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
@@ -116,7 +82,7 @@ var (
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
microcmdUserChangePassword = cli.Command{
|
subcmdChangePassword = cli.Command{
|
||||||
Name: "change-password",
|
Name: "change-password",
|
||||||
Usage: "Change a user's password",
|
Usage: "Change a user's password",
|
||||||
Action: runChangePassword,
|
Action: runChangePassword,
|
||||||
@@ -134,26 +100,6 @@ var (
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
microcmdUserDelete = cli.Command{
|
|
||||||
Name: "delete",
|
|
||||||
Usage: "Delete specific user by id, name or email",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.Int64Flag{
|
|
||||||
Name: "id",
|
|
||||||
Usage: "ID of user of the user to delete",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "username,u",
|
|
||||||
Usage: "Username of the user to delete",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "email,e",
|
|
||||||
Usage: "Email of the user to delete",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: runDeleteUser,
|
|
||||||
}
|
|
||||||
|
|
||||||
subcmdRepoSyncReleases = cli.Command{
|
subcmdRepoSyncReleases = cli.Command{
|
||||||
Name: "repo-sync-releases",
|
Name: "repo-sync-releases",
|
||||||
Usage: "Synchronize repository releases with tags",
|
Usage: "Synchronize repository releases with tags",
|
||||||
@@ -191,8 +137,6 @@ var (
|
|||||||
cmdAuthUpdateLdapBindDn,
|
cmdAuthUpdateLdapBindDn,
|
||||||
cmdAuthAddLdapSimpleAuth,
|
cmdAuthAddLdapSimpleAuth,
|
||||||
cmdAuthUpdateLdapSimpleAuth,
|
cmdAuthUpdateLdapSimpleAuth,
|
||||||
microcmdAuthAddSMTP,
|
|
||||||
microcmdAuthUpdateSMTP,
|
|
||||||
microcmdAuthList,
|
microcmdAuthList,
|
||||||
microcmdAuthDelete,
|
microcmdAuthDelete,
|
||||||
},
|
},
|
||||||
@@ -293,45 +237,6 @@ var (
|
|||||||
Value: "",
|
Value: "",
|
||||||
Usage: "Use a custom Email URL (option for GitHub)",
|
Usage: "Use a custom Email URL (option for GitHub)",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
|
||||||
Name: "icon-url",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Custom icon URL for OAuth2 login source",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "skip-local-2fa",
|
|
||||||
Usage: "Set to true to skip local 2fa for users authenticated by this source",
|
|
||||||
},
|
|
||||||
cli.StringSliceFlag{
|
|
||||||
Name: "scopes",
|
|
||||||
Value: nil,
|
|
||||||
Usage: "Scopes to request when to authenticate against this OAuth2 source",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "required-claim-name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Claim name that has to be set to allow users to login with this source",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "required-claim-value",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Claim value that has to be set to allow users to login with this source",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "group-claim-name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Claim name providing group names for this source",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "admin-group",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Group Claim value for administrator users",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "restricted-group",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Group Claim value for restricted users",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
microcmdAuthUpdateOauth = cli.Command{
|
microcmdAuthUpdateOauth = cli.Command{
|
||||||
@@ -347,94 +252,6 @@ var (
|
|||||||
Action: runAddOauth,
|
Action: runAddOauth,
|
||||||
Flags: oauthCLIFlags,
|
Flags: oauthCLIFlags,
|
||||||
}
|
}
|
||||||
|
|
||||||
subcmdSendMail = cli.Command{
|
|
||||||
Name: "sendmail",
|
|
||||||
Usage: "Send a message to all users",
|
|
||||||
Action: runSendMail,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "title",
|
|
||||||
Usage: `a title of a message`,
|
|
||||||
Value: "",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "content",
|
|
||||||
Usage: "a content of a message",
|
|
||||||
Value: "",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "force,f",
|
|
||||||
Usage: "A flag to bypass a confirmation step",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
smtpCLIFlags = []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Application Name",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "auth-type",
|
|
||||||
Value: "PLAIN",
|
|
||||||
Usage: "SMTP Authentication Type (PLAIN/LOGIN/CRAM-MD5) default PLAIN",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "host",
|
|
||||||
Value: "",
|
|
||||||
Usage: "SMTP Host",
|
|
||||||
},
|
|
||||||
cli.IntFlag{
|
|
||||||
Name: "port",
|
|
||||||
Usage: "SMTP Port",
|
|
||||||
},
|
|
||||||
cli.BoolTFlag{
|
|
||||||
Name: "force-smtps",
|
|
||||||
Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.",
|
|
||||||
},
|
|
||||||
cli.BoolTFlag{
|
|
||||||
Name: "skip-verify",
|
|
||||||
Usage: "Skip TLS verify.",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "helo-hostname",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Hostname sent with HELO. Leave blank to send current hostname",
|
|
||||||
},
|
|
||||||
cli.BoolTFlag{
|
|
||||||
Name: "disable-helo",
|
|
||||||
Usage: "Disable SMTP helo.",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "allowed-domains",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Leave empty to allow all domains. Separate multiple domains with a comma (',')",
|
|
||||||
},
|
|
||||||
cli.BoolTFlag{
|
|
||||||
Name: "skip-local-2fa",
|
|
||||||
Usage: "Skip 2FA to log on.",
|
|
||||||
},
|
|
||||||
cli.BoolTFlag{
|
|
||||||
Name: "active",
|
|
||||||
Usage: "This Authentication Source is Activated.",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdAuthAddSMTP = cli.Command{
|
|
||||||
Name: "add-smtp",
|
|
||||||
Usage: "Add new SMTP authentication source",
|
|
||||||
Action: runAddSMTP,
|
|
||||||
Flags: smtpCLIFlags,
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdAuthUpdateSMTP = cli.Command{
|
|
||||||
Name: "update-smtp",
|
|
||||||
Usage: "Update existing SMTP authentication source",
|
|
||||||
Action: runUpdateSMTP,
|
|
||||||
Flags: append(smtpCLIFlags[:1], append([]cli.Flag{idFlag}, smtpCLIFlags[1:]...)...),
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func runChangePassword(c *cli.Context) error {
|
func runChangePassword(c *cli.Context) error {
|
||||||
@@ -442,36 +259,23 @@ func runChangePassword(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
if err := initDB(); err != nil {
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(c.String("password")) < setting.MinPasswordLength {
|
|
||||||
return fmt.Errorf("Password is not long enough. Needs to be at least %d", setting.MinPasswordLength)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !pwd.IsComplexEnough(c.String("password")) {
|
if !pwd.IsComplexEnough(c.String("password")) {
|
||||||
return errors.New("Password does not meet complexity requirements")
|
return errors.New("Password does not meet complexity requirements")
|
||||||
}
|
}
|
||||||
pwned, err := pwd.IsPwned(context.Background(), c.String("password"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if pwned {
|
|
||||||
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")
|
uname := c.String("username")
|
||||||
user, err := user_model.GetUserByName(uname)
|
user, err := models.GetUserByName(uname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = user.SetPassword(c.String("password")); err != nil {
|
if user.Salt, err = models.GetUserSalt(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
user.HashPassword(c.String("password"))
|
||||||
|
|
||||||
if err = user_model.UpdateUserCols(db.DefaultContext, user, "passwd", "passwd_hash_algo", "salt"); err != nil {
|
if err := models.UpdateUserCols(user, "passwd", "salt"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,10 +307,7 @@ func runCreateUser(c *cli.Context) error {
|
|||||||
fmt.Fprintf(os.Stderr, "--name flag is deprecated. Use --username instead.\n")
|
fmt.Fprintf(os.Stderr, "--name flag is deprecated. Use --username instead.\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
if err := initDB(); err != nil {
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,7 +330,7 @@ func runCreateUser(c *cli.Context) error {
|
|||||||
|
|
||||||
// If this is the first user being created.
|
// If this is the first user being created.
|
||||||
// Take it as the admin and don't force a password update.
|
// Take it as the admin and don't force a password update.
|
||||||
if n := user_model.CountUsers(); n == 0 {
|
if n := models.CountUsers(); n == 0 {
|
||||||
changePassword = false
|
changePassword = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -537,7 +338,7 @@ func runCreateUser(c *cli.Context) error {
|
|||||||
changePassword = c.Bool("must-change-password")
|
changePassword = c.Bool("must-change-password")
|
||||||
}
|
}
|
||||||
|
|
||||||
u := &user_model.User{
|
u := &models.User{
|
||||||
Name: username,
|
Name: username,
|
||||||
Email: c.String("email"),
|
Email: c.String("email"),
|
||||||
Passwd: password,
|
Passwd: password,
|
||||||
@@ -547,7 +348,7 @@ func runCreateUser(c *cli.Context) error {
|
|||||||
Theme: setting.UI.DefaultTheme,
|
Theme: setting.UI.DefaultTheme,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := user_model.CreateUser(u); err != nil {
|
if err := models.CreateUser(u); err != nil {
|
||||||
return fmt.Errorf("CreateUser: %v", err)
|
return fmt.Errorf("CreateUser: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -568,93 +369,15 @@ func runCreateUser(c *cli.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runListUsers(c *cli.Context) error {
|
func runRepoSyncReleases(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
if err := initDB(); err != nil {
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
users, err := user_model.GetAllUsers()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0)
|
|
||||||
|
|
||||||
if c.IsSet("admin") {
|
|
||||||
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\n")
|
|
||||||
for _, u := range users {
|
|
||||||
if u.IsAdmin {
|
|
||||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", u.ID, u.Name, u.Email, u.IsActive)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
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\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Flush()
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func runDeleteUser(c *cli.Context) error {
|
|
||||||
if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
|
|
||||||
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 {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := storage.Init(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var user *user_model.User
|
|
||||||
if c.IsSet("email") {
|
|
||||||
user, err = user_model.GetUserByEmail(c.String("email"))
|
|
||||||
} else if c.IsSet("username") {
|
|
||||||
user, err = user_model.GetUserByName(c.String("username"))
|
|
||||||
} else {
|
|
||||||
user, err = user_model.GetUserByID(c.Int64("id"))
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) {
|
|
||||||
return fmt.Errorf("The user %s who has email %s does not match the provided username %s", user.Name, c.String("email"), c.String("username"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("id") && user.ID != c.Int64("id") {
|
|
||||||
return fmt.Errorf("The user %s does not match the provided id %d", user.Name, c.Int64("id"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return user_service.DeleteUser(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
func runRepoSyncReleases(_ *cli.Context) error {
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace("Synchronizing repository releases (this may take a while)")
|
log.Trace("Synchronizing repository releases (this may take a while)")
|
||||||
for page := 1; ; page++ {
|
for page := 1; ; page++ {
|
||||||
repos, count, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
|
repos, count, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
|
||||||
ListOptions: db.ListOptions{
|
ListOptions: models.ListOptions{
|
||||||
PageSize: models.RepositoryListDefaultPageSize,
|
PageSize: models.RepositoryListDefaultPageSize,
|
||||||
Page: page,
|
Page: page,
|
||||||
},
|
},
|
||||||
@@ -712,27 +435,21 @@ func getReleaseCount(id int64) (int64, error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runRegenerateHooks(_ *cli.Context) error {
|
func runRegenerateHooks(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
if err := initDB(); err != nil {
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
|
return repo_module.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
func runRegenerateKeys(_ *cli.Context) error {
|
func runRegenerateKeys(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
if err := initDB(); err != nil {
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
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
|
var customURLMapping *oauth2.CustomURLMapping
|
||||||
if c.IsSet("use-custom-urls") {
|
if c.IsSet("use-custom-urls") {
|
||||||
customURLMapping = &oauth2.CustomURLMapping{
|
customURLMapping = &oauth2.CustomURLMapping{
|
||||||
@@ -744,36 +461,25 @@ func parseOAuth2Config(c *cli.Context) *oauth2.Source {
|
|||||||
} else {
|
} else {
|
||||||
customURLMapping = nil
|
customURLMapping = nil
|
||||||
}
|
}
|
||||||
return &oauth2.Source{
|
return &models.OAuth2Config{
|
||||||
Provider: c.String("provider"),
|
Provider: c.String("provider"),
|
||||||
ClientID: c.String("key"),
|
ClientID: c.String("key"),
|
||||||
ClientSecret: c.String("secret"),
|
ClientSecret: c.String("secret"),
|
||||||
OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
|
OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
|
||||||
CustomURLMapping: customURLMapping,
|
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 {
|
func runAddOauth(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
if err := initDB(); err != nil {
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return auth.CreateSource(&auth.Source{
|
return models.CreateLoginSource(&models.LoginSource{
|
||||||
Type: auth.OAuth2,
|
Type: models.LoginOAuth2,
|
||||||
Name: c.String("name"),
|
Name: c.String("name"),
|
||||||
IsActive: true,
|
IsActived: true,
|
||||||
Cfg: parseOAuth2Config(c),
|
Cfg: parseOAuth2Config(c),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -782,19 +488,16 @@ func runUpdateOauth(c *cli.Context) error {
|
|||||||
return fmt.Errorf("--id flag is missing")
|
return fmt.Errorf("--id flag is missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
if err := initDB(); err != nil {
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
source, err := auth.GetSourceByID(c.Int64("id"))
|
source, err := models.GetLoginSourceByID(c.Int64("id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
oAuth2Config := source.Cfg.(*oauth2.Source)
|
oAuth2Config := source.OAuth2()
|
||||||
|
|
||||||
if c.IsSet("name") {
|
if c.IsSet("name") {
|
||||||
source.Name = c.String("name")
|
source.Name = c.String("name")
|
||||||
@@ -816,32 +519,6 @@ func runUpdateOauth(c *cli.Context) error {
|
|||||||
oAuth2Config.OpenIDConnectAutoDiscoveryURL = c.String("auto-discover-url")
|
oAuth2Config.OpenIDConnectAutoDiscoveryURL = c.String("auto-discover-url")
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.IsSet("icon-url") {
|
|
||||||
oAuth2Config.IconURL = c.String("icon-url")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("scopes") {
|
|
||||||
oAuth2Config.Scopes = c.StringSlice("scopes")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("required-claim-name") {
|
|
||||||
oAuth2Config.RequiredClaimName = c.String("required-claim-name")
|
|
||||||
|
|
||||||
}
|
|
||||||
if c.IsSet("required-claim-value") {
|
|
||||||
oAuth2Config.RequiredClaimValue = c.String("required-claim-value")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("group-claim-name") {
|
|
||||||
oAuth2Config.GroupClaimName = c.String("group-claim-name")
|
|
||||||
}
|
|
||||||
if c.IsSet("admin-group") {
|
|
||||||
oAuth2Config.AdminGroup = c.String("admin-group")
|
|
||||||
}
|
|
||||||
if c.IsSet("restricted-group") {
|
|
||||||
oAuth2Config.RestrictedGroup = c.String("restricted-group")
|
|
||||||
}
|
|
||||||
|
|
||||||
// update custom URL mapping
|
// update custom URL mapping
|
||||||
var customURLMapping = &oauth2.CustomURLMapping{}
|
var customURLMapping = &oauth2.CustomURLMapping{}
|
||||||
|
|
||||||
@@ -870,130 +547,15 @@ func runUpdateOauth(c *cli.Context) error {
|
|||||||
oAuth2Config.CustomURLMapping = customURLMapping
|
oAuth2Config.CustomURLMapping = customURLMapping
|
||||||
source.Cfg = oAuth2Config
|
source.Cfg = oAuth2Config
|
||||||
|
|
||||||
return auth.UpdateSource(source)
|
return models.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")
|
|
||||||
}
|
|
||||||
var 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.CreateSource(&auth.Source{
|
|
||||||
Type: auth.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.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.UpdateSource(source)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runListAuth(c *cli.Context) error {
|
func runListAuth(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
if err := initDB(); err != nil {
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
authSources, err := auth.Sources()
|
loginSources, err := models.LoginSources()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -1012,8 +574,8 @@ func runListAuth(c *cli.Context) error {
|
|||||||
// loop through each source and print
|
// loop through each source and print
|
||||||
w := tabwriter.NewWriter(os.Stdout, c.Int("min-width"), c.Int("tab-width"), c.Int("padding"), padChar, flags)
|
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")
|
fmt.Fprintf(w, "ID\tName\tType\tEnabled\n")
|
||||||
for _, source := range authSources {
|
for _, source := range loginSources {
|
||||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, source.Type.String(), source.IsActive)
|
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, models.LoginNames[source.Type], source.IsActived)
|
||||||
}
|
}
|
||||||
w.Flush()
|
w.Flush()
|
||||||
|
|
||||||
@@ -1025,17 +587,14 @@ func runDeleteAuth(c *cli.Context) error {
|
|||||||
return fmt.Errorf("--id flag is missing")
|
return fmt.Errorf("--id flag is missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
if err := initDB(); err != nil {
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(ctx); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
source, err := auth.GetSourceByID(c.Int64("id"))
|
source, err := models.GetLoginSourceByID(c.Int64("id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return auth_service.DeleteSource(source)
|
return models.DeleteSource(source)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,22 +5,21 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/auth"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/services/auth/source/ldap"
|
"code.gitea.io/gitea/modules/auth/ldap"
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
authService struct {
|
authService struct {
|
||||||
initDB func(ctx context.Context) error
|
initDB func() error
|
||||||
createAuthSource func(*auth.Source) error
|
createLoginSource func(loginSource *models.LoginSource) error
|
||||||
updateAuthSource func(*auth.Source) error
|
updateLoginSource func(loginSource *models.LoginSource) error
|
||||||
getAuthSourceByID func(id int64) (*auth.Source, error)
|
getLoginSourceByID func(id int64) (*models.LoginSource, error)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -90,14 +89,6 @@ var (
|
|||||||
Name: "public-ssh-key-attribute",
|
Name: "public-ssh-key-attribute",
|
||||||
Usage: "The attribute of the user’s LDAP record containing the user’s public ssh key.",
|
Usage: "The attribute of the user’s LDAP record containing the user’s public ssh key.",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
|
||||||
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,
|
ldapBindDnCLIFlags = append(commonLdapCLIFlags,
|
||||||
@@ -168,106 +159,99 @@ var (
|
|||||||
// newAuthService creates a service with default functions.
|
// newAuthService creates a service with default functions.
|
||||||
func newAuthService() *authService {
|
func newAuthService() *authService {
|
||||||
return &authService{
|
return &authService{
|
||||||
initDB: initDB,
|
initDB: initDB,
|
||||||
createAuthSource: auth.CreateSource,
|
createLoginSource: models.CreateLoginSource,
|
||||||
updateAuthSource: auth.UpdateSource,
|
updateLoginSource: models.UpdateSource,
|
||||||
getAuthSourceByID: auth.GetSourceByID,
|
getLoginSourceByID: models.GetLoginSourceByID,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseAuthSource assigns values on authSource according to command line flags.
|
// parseLoginSource assigns values on loginSource according to command line flags.
|
||||||
func parseAuthSource(c *cli.Context, authSource *auth.Source) {
|
func parseLoginSource(c *cli.Context, loginSource *models.LoginSource) {
|
||||||
if c.IsSet("name") {
|
if c.IsSet("name") {
|
||||||
authSource.Name = c.String("name")
|
loginSource.Name = c.String("name")
|
||||||
}
|
}
|
||||||
if c.IsSet("not-active") {
|
if c.IsSet("not-active") {
|
||||||
authSource.IsActive = !c.Bool("not-active")
|
loginSource.IsActived = !c.Bool("not-active")
|
||||||
}
|
}
|
||||||
if c.IsSet("synchronize-users") {
|
if c.IsSet("synchronize-users") {
|
||||||
authSource.IsSyncEnabled = c.Bool("synchronize-users")
|
loginSource.IsSyncEnabled = c.Bool("synchronize-users")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseLdapConfig assigns values on config according to command line flags.
|
// 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") {
|
if c.IsSet("name") {
|
||||||
config.Name = c.String("name")
|
config.Source.Name = c.String("name")
|
||||||
}
|
}
|
||||||
if c.IsSet("host") {
|
if c.IsSet("host") {
|
||||||
config.Host = c.String("host")
|
config.Source.Host = c.String("host")
|
||||||
}
|
}
|
||||||
if c.IsSet("port") {
|
if c.IsSet("port") {
|
||||||
config.Port = c.Int("port")
|
config.Source.Port = c.Int("port")
|
||||||
}
|
}
|
||||||
if c.IsSet("security-protocol") {
|
if c.IsSet("security-protocol") {
|
||||||
p, ok := findLdapSecurityProtocolByName(c.String("security-protocol"))
|
p, ok := findLdapSecurityProtocolByName(c.String("security-protocol"))
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("Unknown security protocol name: %s", c.String("security-protocol"))
|
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") {
|
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") {
|
if c.IsSet("bind-dn") {
|
||||||
config.BindDN = c.String("bind-dn")
|
config.Source.BindDN = c.String("bind-dn")
|
||||||
}
|
}
|
||||||
if c.IsSet("user-dn") {
|
if c.IsSet("user-dn") {
|
||||||
config.UserDN = c.String("user-dn")
|
config.Source.UserDN = c.String("user-dn")
|
||||||
}
|
}
|
||||||
if c.IsSet("bind-password") {
|
if c.IsSet("bind-password") {
|
||||||
config.BindPassword = c.String("bind-password")
|
config.Source.BindPassword = c.String("bind-password")
|
||||||
}
|
}
|
||||||
if c.IsSet("user-search-base") {
|
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") {
|
if c.IsSet("username-attribute") {
|
||||||
config.AttributeUsername = c.String("username-attribute")
|
config.Source.AttributeUsername = c.String("username-attribute")
|
||||||
}
|
}
|
||||||
if c.IsSet("firstname-attribute") {
|
if c.IsSet("firstname-attribute") {
|
||||||
config.AttributeName = c.String("firstname-attribute")
|
config.Source.AttributeName = c.String("firstname-attribute")
|
||||||
}
|
}
|
||||||
if c.IsSet("surname-attribute") {
|
if c.IsSet("surname-attribute") {
|
||||||
config.AttributeSurname = c.String("surname-attribute")
|
config.Source.AttributeSurname = c.String("surname-attribute")
|
||||||
}
|
}
|
||||||
if c.IsSet("email-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") {
|
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") {
|
if c.IsSet("public-ssh-key-attribute") {
|
||||||
config.AttributeSSHPublicKey = c.String("public-ssh-key-attribute")
|
config.Source.AttributeSSHPublicKey = c.String("public-ssh-key-attribute")
|
||||||
}
|
|
||||||
if c.IsSet("avatar-attribute") {
|
|
||||||
config.AttributeAvatar = c.String("avatar-attribute")
|
|
||||||
}
|
}
|
||||||
if c.IsSet("page-size") {
|
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") {
|
if c.IsSet("user-filter") {
|
||||||
config.Filter = c.String("user-filter")
|
config.Source.Filter = c.String("user-filter")
|
||||||
}
|
}
|
||||||
if c.IsSet("admin-filter") {
|
if c.IsSet("admin-filter") {
|
||||||
config.AdminFilter = c.String("admin-filter")
|
config.Source.AdminFilter = c.String("admin-filter")
|
||||||
}
|
}
|
||||||
if c.IsSet("restricted-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") {
|
if c.IsSet("allow-deactivate-all") {
|
||||||
config.AllowDeactivateAll = c.Bool("allow-deactivate-all")
|
config.Source.AllowDeactivateAll = c.Bool("allow-deactivate-all")
|
||||||
}
|
}
|
||||||
if c.IsSet("skip-local-2fa") {
|
|
||||||
config.SkipLocalTwoFA = c.Bool("skip-local-2fa")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// findLdapSecurityProtocolByName finds security protocol by its name ignoring case.
|
// findLdapSecurityProtocolByName finds security protocol by its name ignoring case.
|
||||||
// It returns the value of the security protocol and if it was found.
|
// It returns the value of the security protocol and if it was found.
|
||||||
func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
|
func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
|
||||||
for i, n := range ldap.SecurityProtocolNames {
|
for i, n := range models.SecurityProtocolNames {
|
||||||
if strings.EqualFold(name, n) {
|
if strings.EqualFold(name, n) {
|
||||||
return i, true
|
return i, true
|
||||||
}
|
}
|
||||||
@@ -275,23 +259,23 @@ func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
|
|||||||
return 0, false
|
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.
|
// 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 {
|
if err := argsSet(c, "id"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
authSource, err := a.getAuthSourceByID(c.Int64("id"))
|
loginSource, err := a.getLoginSourceByID(c.Int64("id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if authSource.Type != authType {
|
if loginSource.Type != loginType {
|
||||||
return nil, fmt.Errorf("Invalid authentication type. expected: %s, actual: %s", authType.String(), authSource.Type.String())
|
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.
|
// addLdapBindDn adds a new LDAP via Bind DN authentication source.
|
||||||
@@ -300,49 +284,45 @@ func (a *authService) addLdapBindDn(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
if err := a.initDB(); err != nil {
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := a.initDB(ctx); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
authSource := &auth.Source{
|
loginSource := &models.LoginSource{
|
||||||
Type: auth.LDAP,
|
Type: models.LoginLDAP,
|
||||||
IsActive: true, // active by default
|
IsActived: true, // active by default
|
||||||
Cfg: &ldap.Source{
|
Cfg: &models.LDAPConfig{
|
||||||
Enabled: true, // always true
|
Source: &ldap.Source{
|
||||||
|
Enabled: true, // always true
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
parseAuthSource(c, authSource)
|
parseLoginSource(c, loginSource)
|
||||||
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.createAuthSource(authSource)
|
return a.createLoginSource(loginSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateLdapBindDn updates a new LDAP via Bind DN authentication source.
|
// updateLdapBindDn updates a new LDAP via Bind DN authentication source.
|
||||||
func (a *authService) updateLdapBindDn(c *cli.Context) error {
|
func (a *authService) updateLdapBindDn(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
if err := a.initDB(); err != nil {
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := a.initDB(ctx); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
authSource, err := a.getAuthSource(c, auth.LDAP)
|
loginSource, err := a.getLoginSource(c, models.LoginLDAP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
parseAuthSource(c, authSource)
|
parseLoginSource(c, loginSource)
|
||||||
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.updateAuthSource(authSource)
|
return a.updateLoginSource(loginSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
|
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
|
||||||
@@ -351,47 +331,43 @@ func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
if err := a.initDB(); err != nil {
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := a.initDB(ctx); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
authSource := &auth.Source{
|
loginSource := &models.LoginSource{
|
||||||
Type: auth.DLDAP,
|
Type: models.LoginDLDAP,
|
||||||
IsActive: true, // active by default
|
IsActived: true, // active by default
|
||||||
Cfg: &ldap.Source{
|
Cfg: &models.LDAPConfig{
|
||||||
Enabled: true, // always true
|
Source: &ldap.Source{
|
||||||
|
Enabled: true, // always true
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
parseAuthSource(c, authSource)
|
parseLoginSource(c, loginSource)
|
||||||
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.createAuthSource(authSource)
|
return a.createLoginSource(loginSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateLdapBindDn updates a new LDAP (simple auth) authentication source.
|
// updateLdapBindDn updates a new LDAP (simple auth) authentication source.
|
||||||
func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
|
func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
if err := a.initDB(); err != nil {
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := a.initDB(ctx); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
authSource, err := a.getAuthSource(c, auth.DLDAP)
|
loginSource, err := a.getLoginSource(c, models.LoginDLDAP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
parseAuthSource(c, authSource)
|
parseLoginSource(c, loginSource)
|
||||||
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.updateAuthSource(authSource)
|
return a.updateLoginSource(loginSource)
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
64
cmd/cmd.go
64
cmd/cmd.go
@@ -7,16 +7,10 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
@@ -38,59 +32,17 @@ func argsSet(c *cli.Context, args ...string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// confirm waits for user input which confirms an action
|
func initDB() error {
|
||||||
func confirm() (bool, error) {
|
return initDBDisableConsole(false)
|
||||||
var response string
|
|
||||||
|
|
||||||
_, err := fmt.Scanln(&response)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch strings.ToLower(response) {
|
|
||||||
case "y", "yes":
|
|
||||||
return true, nil
|
|
||||||
case "n", "no":
|
|
||||||
return false, nil
|
|
||||||
default:
|
|
||||||
return false, errors.New(response + " isn't a correct confirmation string")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initDB(ctx context.Context) error {
|
func initDBDisableConsole(disableConsole bool) error {
|
||||||
setting.LoadFromExisting()
|
setting.NewContext()
|
||||||
setting.InitDBConfig()
|
setting.InitDBConfig()
|
||||||
setting.NewXORMLogService(false)
|
|
||||||
|
|
||||||
if setting.Database.Type == "" {
|
setting.NewXORMLogService(disableConsole)
|
||||||
log.Fatal(`Database settings are missing from the configuration file: %q.
|
if err := models.SetEngine(); err != nil {
|
||||||
Ensure you are running in the correct environment or set the correct configuration file with -c.
|
return fmt.Errorf("models.SetEngine: %v", err)
|
||||||
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 initialise the database using the configuration in %q. Error: %v", setting.CustomConf, err)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func installSignals() (context.Context, context.CancelFunc) {
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
go func() {
|
|
||||||
// install notify
|
|
||||||
signalChannel := make(chan os.Signal, 1)
|
|
||||||
|
|
||||||
signal.Notify(
|
|
||||||
signalChannel,
|
|
||||||
syscall.SIGINT,
|
|
||||||
syscall.SIGTERM,
|
|
||||||
)
|
|
||||||
select {
|
|
||||||
case <-signalChannel:
|
|
||||||
case <-ctx.Done():
|
|
||||||
}
|
|
||||||
cancel()
|
|
||||||
signal.Reset()
|
|
||||||
}()
|
|
||||||
|
|
||||||
return ctx, cancel
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
@@ -23,25 +23,22 @@ var CmdConvert = cli.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runConvert(ctx *cli.Context) error {
|
func runConvert(ctx *cli.Context) error {
|
||||||
stdCtx, cancel := installSignals()
|
if err := initDB(); err != nil {
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(stdCtx); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("AppPath: %s", setting.AppPath)
|
log.Trace("AppPath: %s", setting.AppPath)
|
||||||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
log.Trace("AppWorkPath: %s", setting.AppWorkPath)
|
||||||
log.Info("Custom path: %s", setting.CustomPath)
|
log.Trace("Custom path: %s", setting.CustomPath)
|
||||||
log.Info("Log path: %s", setting.LogRootPath)
|
log.Trace("Log path: %s", setting.LogRootPath)
|
||||||
log.Info("Configuration file: %s", setting.CustomConf)
|
setting.InitDBConfig()
|
||||||
|
|
||||||
if !setting.Database.UseMySQL {
|
if !setting.Database.UseMySQL {
|
||||||
fmt.Println("This command can only be used with a MySQL database")
|
fmt.Println("This command can only be used with a MySQL database")
|
||||||
return nil
|
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)
|
log.Fatal("Failed to convert database from utf8 to utf8mb4: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
65
cmd/docs.go
65
cmd/docs.go
@@ -1,65 +0,0 @@
|
|||||||
// 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 (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CmdDocs represents the available docs sub-command.
|
|
||||||
var CmdDocs = cli.Command{
|
|
||||||
Name: "docs",
|
|
||||||
Usage: "Output CLI documentation",
|
|
||||||
Description: "A command to output Gitea's CLI documentation, optionally to a file.",
|
|
||||||
Action: runDocs,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "man",
|
|
||||||
Usage: "Output man pages instead",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "output, o",
|
|
||||||
Usage: "Path to output to instead of stdout (will overwrite if exists)",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func runDocs(ctx *cli.Context) error {
|
|
||||||
docs, err := ctx.App.ToMarkdown()
|
|
||||||
if ctx.Bool("man") {
|
|
||||||
docs, err = ctx.App.ToMan()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ctx.Bool("man") {
|
|
||||||
// Clean up markdown. The following bug was fixed in v2, but is present in v1.
|
|
||||||
// It affects markdown output (even though the issue is referring to man pages)
|
|
||||||
// https://github.com/urfave/cli/issues/1040
|
|
||||||
firstHashtagIndex := strings.Index(docs, "#")
|
|
||||||
|
|
||||||
if firstHashtagIndex > 0 {
|
|
||||||
docs = docs[firstHashtagIndex:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out := os.Stdout
|
|
||||||
if ctx.String("output") != "" {
|
|
||||||
fi, err := os.Create(ctx.String("output"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer fi.Close()
|
|
||||||
out = fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = fmt.Fprintln(out, docs)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
579
cmd/doctor.go
579
cmd/doctor.go
@@ -5,21 +5,28 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
golog "log"
|
golog "log"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/models/migrations"
|
"code.gitea.io/gitea/models/migrations"
|
||||||
"code.gitea.io/gitea/modules/doctor"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/options"
|
||||||
|
"code.gitea.io/gitea/modules/repository"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"xorm.io/builder"
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
"xorm.io/xorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdDoctor represents the available doctor sub-command.
|
// CmdDoctor represents the available doctor sub-command.
|
||||||
@@ -53,101 +60,85 @@ var CmdDoctor = cli.Command{
|
|||||||
Name: "log-file",
|
Name: "log-file",
|
||||||
Usage: `Name of the log file (default: "doctor.log"). Set to "-" to output to stdout, set to "" to disable`,
|
Usage: `Name of the log file (default: "doctor.log"). Set to "-" to output to stdout, set to "" to disable`,
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "color, H",
|
|
||||||
Usage: "Use color for outputted information",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Subcommands: []cli.Command{
|
|
||||||
cmdRecreateTable,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmdRecreateTable = cli.Command{
|
type check struct {
|
||||||
Name: "recreate-table",
|
title string
|
||||||
Usage: "Recreate tables from XORM definitions and copy the data.",
|
name string
|
||||||
ArgsUsage: "[TABLE]... : (TABLEs to recreate - leave blank for all)",
|
isDefault bool
|
||||||
Flags: []cli.Flag{
|
f func(ctx *cli.Context) ([]string, error)
|
||||||
cli.BoolFlag{
|
abortIfFailed bool
|
||||||
Name: "debug",
|
skipDatabaseInit bool
|
||||||
Usage: "Print SQL commands sent",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Description: `The database definitions Gitea uses change across versions, sometimes changing default values and leaving old unused columns.
|
|
||||||
|
|
||||||
This command will cause Xorm to recreate tables, copying over the data and deleting the old table.
|
|
||||||
|
|
||||||
You should back-up your database before doing this and ensure that your database is up-to-date first.`,
|
|
||||||
Action: runRecreateTable,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runRecreateTable(ctx *cli.Context) error {
|
// checklist represents list for all checks
|
||||||
// Redirect the default golog to here
|
var checklist = []check{
|
||||||
golog.SetFlags(0)
|
{
|
||||||
golog.SetPrefix("")
|
// NOTE: this check should be the first in the list
|
||||||
golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT)))
|
title: "Check paths and basic configuration",
|
||||||
|
name: "paths",
|
||||||
setting.LoadFromExisting()
|
isDefault: true,
|
||||||
setting.InitDBConfig()
|
f: runDoctorPathInfo,
|
||||||
|
abortIfFailed: true,
|
||||||
setting.EnableXORMLog = ctx.Bool("debug")
|
skipDatabaseInit: true,
|
||||||
setting.Database.LogSQL = ctx.Bool("debug")
|
},
|
||||||
setting.Cfg.Section("log").Key("XORM").SetValue(",")
|
{
|
||||||
|
title: "Check Database Version",
|
||||||
setting.NewXORMLogService(!ctx.Bool("debug"))
|
name: "check-db-version",
|
||||||
stdCtx, cancel := installSignals()
|
isDefault: true,
|
||||||
defer cancel()
|
f: runDoctorCheckDBVersion,
|
||||||
|
abortIfFailed: false,
|
||||||
if err := db.InitEngine(stdCtx); 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.")
|
title: "Check consistency of database",
|
||||||
return nil
|
name: "check-db-consistency",
|
||||||
}
|
isDefault: false,
|
||||||
|
f: runDoctorCheckDBConsistency,
|
||||||
args := ctx.Args()
|
},
|
||||||
names := make([]string, 0, ctx.NArg())
|
{
|
||||||
for i := 0; i < ctx.NArg(); i++ {
|
title: "Check if OpenSSH authorized_keys file is up-to-date",
|
||||||
names = append(names, args.Get(i))
|
name: "authorized_keys",
|
||||||
}
|
isDefault: true,
|
||||||
|
f: runDoctorAuthorizedKeys,
|
||||||
beans, err := db.NamesToBean(names...)
|
},
|
||||||
if err != nil {
|
{
|
||||||
return err
|
title: "Check if SCRIPT_TYPE is available",
|
||||||
}
|
name: "script-type",
|
||||||
recreateTables := migrations.RecreateTables(beans...)
|
isDefault: false,
|
||||||
|
f: runDoctorScriptType,
|
||||||
return db.InitEngineWithMigration(context.Background(), func(x *xorm.Engine) error {
|
},
|
||||||
if err := migrations.EnsureUpToDate(x); err != nil {
|
{
|
||||||
return err
|
title: "Check if hook files are up-to-date and executable",
|
||||||
}
|
name: "hooks",
|
||||||
return recreateTables(x)
|
isDefault: false,
|
||||||
})
|
f: runDoctorHooks,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Recalculate merge bases",
|
||||||
|
name: "recalculate_merge_bases",
|
||||||
|
isDefault: false,
|
||||||
|
f: runDoctorPRMergeBase,
|
||||||
|
},
|
||||||
|
// more checks please append here
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDoctor(ctx *cli.Context) error {
|
func runDoctor(ctx *cli.Context) error {
|
||||||
|
|
||||||
// Silence the default loggers
|
// Silence the default loggers
|
||||||
log.DelNamedLogger("console")
|
log.DelNamedLogger("console")
|
||||||
log.DelNamedLogger(log.DEFAULT)
|
log.DelNamedLogger(log.DEFAULT)
|
||||||
|
|
||||||
stdCtx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
// Now setup our own
|
// Now setup our own
|
||||||
logFile := ctx.String("log-file")
|
logFile := ctx.String("log-file")
|
||||||
if !ctx.IsSet("log-file") {
|
if !ctx.IsSet("log-file") {
|
||||||
logFile = "doctor.log"
|
logFile = "doctor.log"
|
||||||
}
|
}
|
||||||
|
|
||||||
colorize := log.CanColorStdout
|
|
||||||
if ctx.IsSet("color") {
|
|
||||||
colorize = ctx.Bool("color")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(logFile) == 0 {
|
if len(logFile) == 0 {
|
||||||
log.NewLogger(1000, "doctor", "console", fmt.Sprintf(`{"level":"NONE","stacktracelevel":"NONE","colorize":%t}`, colorize))
|
log.NewLogger(1000, "doctor", "console", `{"level":"NONE","stacktracelevel":"NONE","colorize":"%t"}`)
|
||||||
} else if logFile == "-" {
|
} else if logFile == "-" {
|
||||||
log.NewLogger(1000, "doctor", "console", fmt.Sprintf(`{"level":"trace","stacktracelevel":"NONE","colorize":%t}`, colorize))
|
log.NewLogger(1000, "doctor", "console", `{"level":"trace","stacktracelevel":"NONE"}`)
|
||||||
} else {
|
} else {
|
||||||
log.NewLogger(1000, "doctor", "file", fmt.Sprintf(`{"filename":%q,"level":"trace","stacktracelevel":"NONE"}`, logFile))
|
log.NewLogger(1000, "doctor", "file", fmt.Sprintf(`{"filename":%q,"level":"trace","stacktracelevel":"NONE"}`, logFile))
|
||||||
}
|
}
|
||||||
@@ -158,24 +149,24 @@ func runDoctor(ctx *cli.Context) error {
|
|||||||
golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT)))
|
golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT)))
|
||||||
|
|
||||||
if ctx.IsSet("list") {
|
if ctx.IsSet("list") {
|
||||||
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
|
w := tabwriter.NewWriter(os.Stdout, 0, 8, 0, '\t', 0)
|
||||||
_, _ = w.Write([]byte("Default\tName\tTitle\n"))
|
_, _ = w.Write([]byte("Default\tName\tTitle\n"))
|
||||||
for _, check := range doctor.Checks {
|
for _, check := range checklist {
|
||||||
if check.IsDefault {
|
if check.isDefault {
|
||||||
_, _ = w.Write([]byte{'*'})
|
_, _ = w.Write([]byte{'*'})
|
||||||
}
|
}
|
||||||
_, _ = w.Write([]byte{'\t'})
|
_, _ = w.Write([]byte{'\t'})
|
||||||
_, _ = w.Write([]byte(check.Name))
|
_, _ = w.Write([]byte(check.name))
|
||||||
_, _ = w.Write([]byte{'\t'})
|
_, _ = w.Write([]byte{'\t'})
|
||||||
_, _ = w.Write([]byte(check.Title))
|
_, _ = w.Write([]byte(check.title))
|
||||||
_, _ = w.Write([]byte{'\n'})
|
_, _ = w.Write([]byte{'\n'})
|
||||||
}
|
}
|
||||||
return w.Flush()
|
return w.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
var checks []*doctor.Check
|
var checks []check
|
||||||
if ctx.Bool("all") {
|
if ctx.Bool("all") {
|
||||||
checks = doctor.Checks
|
checks = checklist
|
||||||
} else if ctx.IsSet("run") {
|
} else if ctx.IsSet("run") {
|
||||||
addDefault := ctx.Bool("default")
|
addDefault := ctx.Bool("default")
|
||||||
names := ctx.StringSlice("run")
|
names := ctx.StringSlice("run")
|
||||||
@@ -183,37 +174,423 @@ func runDoctor(ctx *cli.Context) error {
|
|||||||
names[i] = strings.ToLower(strings.TrimSpace(name))
|
names[i] = strings.ToLower(strings.TrimSpace(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, check := range doctor.Checks {
|
for _, check := range checklist {
|
||||||
if addDefault && check.IsDefault {
|
if addDefault && check.isDefault {
|
||||||
checks = append(checks, check)
|
checks = append(checks, check)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
if name == check.Name {
|
if name == check.name {
|
||||||
checks = append(checks, check)
|
checks = append(checks, check)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, check := range doctor.Checks {
|
for _, check := range checklist {
|
||||||
if check.IsDefault {
|
if check.isDefault {
|
||||||
checks = append(checks, check)
|
checks = append(checks, check)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we can set up our own logger to return information about what the doctor is doing
|
dbIsInit := false
|
||||||
if err := log.NewNamedLogger("doctorouter",
|
|
||||||
1000,
|
for i, check := range checks {
|
||||||
"console",
|
if !dbIsInit && !check.skipDatabaseInit {
|
||||||
"console",
|
// Only open database after the most basic configuration check
|
||||||
fmt.Sprintf(`{"level":"INFO","stacktracelevel":"NONE","colorize":%t,"flags":-1}`, colorize)); err != nil {
|
setting.EnableXORMLog = false
|
||||||
fmt.Println(err)
|
if err := initDBDisableConsole(true); err != nil {
|
||||||
return err
|
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
|
||||||
|
}
|
||||||
|
dbIsInit = true
|
||||||
|
}
|
||||||
|
fmt.Println("[", i+1, "]", check.title)
|
||||||
|
messages, err := check.f(ctx)
|
||||||
|
for _, message := range messages {
|
||||||
|
fmt.Println("-", message)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error:", err)
|
||||||
|
if check.abortIfFailed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println("OK.")
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runDoctorPathInfo(ctx *cli.Context) ([]string, error) {
|
||||||
|
|
||||||
|
res := make([]string, 0, 10)
|
||||||
|
|
||||||
|
if fi, err := os.Stat(setting.CustomConf); err != nil || !fi.Mode().IsRegular() {
|
||||||
|
res = append(res, fmt.Sprintf("Failed to find configuration file at '%s'.", setting.CustomConf))
|
||||||
|
res = append(res, fmt.Sprintf("If you've never ran Gitea yet, this is normal and '%s' will be created for you on first run.", setting.CustomConf))
|
||||||
|
res = append(res, "Otherwise check that you are running this command from the correct path and/or provide a `--config` parameter.")
|
||||||
|
return res, fmt.Errorf("can't proceed without a configuration file")
|
||||||
}
|
}
|
||||||
|
|
||||||
logger := log.GetLogger("doctorouter")
|
setting.NewContext()
|
||||||
defer logger.Close()
|
|
||||||
return doctor.RunChecks(stdCtx, logger, ctx.Bool("fix"), checks)
|
fail := false
|
||||||
|
|
||||||
|
check := func(name, path string, is_dir, required, is_write bool) {
|
||||||
|
res = append(res, fmt.Sprintf("%-25s '%s'", name+":", path))
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) && ctx.Bool("fix") && is_dir {
|
||||||
|
if err := os.MkdirAll(path, 0777); err != nil {
|
||||||
|
res = append(res, fmt.Sprintf(" ERROR: %v", err))
|
||||||
|
fail = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fi, err = os.Stat(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
if required {
|
||||||
|
res = append(res, fmt.Sprintf(" ERROR: %v", err))
|
||||||
|
fail = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res = append(res, fmt.Sprintf(" NOTICE: not accessible (%v)", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_dir && !fi.IsDir() {
|
||||||
|
res = append(res, " ERROR: not a directory")
|
||||||
|
fail = true
|
||||||
|
return
|
||||||
|
} else if !is_dir && !fi.Mode().IsRegular() {
|
||||||
|
res = append(res, " ERROR: not a regular file")
|
||||||
|
fail = true
|
||||||
|
} else if is_write {
|
||||||
|
if err := runDoctorWritableDir(path); err != nil {
|
||||||
|
res = append(res, fmt.Sprintf(" ERROR: not writable: %v", err))
|
||||||
|
fail = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note print paths inside quotes to make any leading/trailing spaces evident
|
||||||
|
check("Configuration File Path", setting.CustomConf, false, true, false)
|
||||||
|
check("Repository Root Path", setting.RepoRootPath, true, true, true)
|
||||||
|
check("Data Root Path", setting.AppDataPath, true, true, true)
|
||||||
|
check("Custom File Root Path", setting.CustomPath, true, false, false)
|
||||||
|
check("Work directory", setting.AppWorkPath, true, true, false)
|
||||||
|
check("Log Root Path", setting.LogRootPath, true, true, true)
|
||||||
|
|
||||||
|
if options.IsDynamic() {
|
||||||
|
// Do not check/report on StaticRootPath if data is embedded in Gitea (-tags bindata)
|
||||||
|
check("Static File Root Path", setting.StaticRootPath, true, true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fail {
|
||||||
|
return res, fmt.Errorf("please check your configuration file and try again")
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runDoctorWritableDir(path string) error {
|
||||||
|
// There's no platform-independent way of checking if a directory is writable
|
||||||
|
// https://stackoverflow.com/questions/20026320/how-to-tell-if-folder-exists-and-is-writable
|
||||||
|
|
||||||
|
tmpFile, err := ioutil.TempFile(path, "doctors-order")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.Remove(tmpFile.Name()); err != nil {
|
||||||
|
fmt.Printf("Warning: can't remove temporary file: '%s'\n", tmpFile.Name())
|
||||||
|
}
|
||||||
|
tmpFile.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const tplCommentPrefix = `# gitea public key`
|
||||||
|
|
||||||
|
func runDoctorAuthorizedKeys(ctx *cli.Context) ([]string, error) {
|
||||||
|
if setting.SSH.StartBuiltinServer || !setting.SSH.CreateAuthorizedKeysFile {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
|
||||||
|
f, err := os.Open(fPath)
|
||||||
|
if err != nil {
|
||||||
|
if ctx.Bool("fix") {
|
||||||
|
return []string{fmt.Sprintf("Error whilst opening authorized_keys: %v. Attempting regeneration", err)}, models.RewriteAllPublicKeys()
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
linesInAuthorizedKeys := map[string]bool{}
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(f)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if strings.HasPrefix(line, tplCommentPrefix) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
linesInAuthorizedKeys[line] = true
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
// now we regenerate and check if there are any lines missing
|
||||||
|
regenerated := &bytes.Buffer{}
|
||||||
|
if err := models.RegeneratePublicKeys(regenerated); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
scanner = bufio.NewScanner(regenerated)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if strings.HasPrefix(line, tplCommentPrefix) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ok := linesInAuthorizedKeys[line]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ctx.Bool("fix") {
|
||||||
|
return []string{"authorized_keys is out of date, attempting regeneration"}, models.RewriteAllPublicKeys()
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf(`authorized_keys is out of date and should be regenerated with "gitea admin regenerate keys" or "gitea doctor --run authorized_keys --fix"`)
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runDoctorCheckDBVersion(ctx *cli.Context) ([]string, error) {
|
||||||
|
if err := models.NewEngine(context.Background(), migrations.EnsureUpToDate); err != nil {
|
||||||
|
if ctx.Bool("fix") {
|
||||||
|
return []string{fmt.Sprintf("WARN: Got Error %v during ensure up to date", err), "Attempting to migrate to the latest DB version to fix this."}, models.NewEngine(context.Background(), migrations.Migrate)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func iterateRepositories(each func(*models.Repository) ([]string, error)) ([]string, error) {
|
||||||
|
results := []string{}
|
||||||
|
err := models.Iterate(
|
||||||
|
models.DefaultDBContext(),
|
||||||
|
new(models.Repository),
|
||||||
|
builder.Gt{"id": 0},
|
||||||
|
func(idx int, bean interface{}) error {
|
||||||
|
res, err := each(bean.(*models.Repository))
|
||||||
|
results = append(results, res...)
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func iteratePRs(repo *models.Repository, each func(*models.Repository, *models.PullRequest) ([]string, error)) ([]string, error) {
|
||||||
|
results := []string{}
|
||||||
|
err := models.Iterate(
|
||||||
|
models.DefaultDBContext(),
|
||||||
|
new(models.PullRequest),
|
||||||
|
builder.Eq{"base_repo_id": repo.ID},
|
||||||
|
func(idx int, bean interface{}) error {
|
||||||
|
res, err := each(repo, bean.(*models.PullRequest))
|
||||||
|
results = append(results, res...)
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func runDoctorHooks(ctx *cli.Context) ([]string, error) {
|
||||||
|
// Need to iterate across all of the repositories
|
||||||
|
return iterateRepositories(func(repo *models.Repository) ([]string, error) {
|
||||||
|
results, err := repository.CheckDelegateHooks(repo.RepoPath())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(results) > 0 && ctx.Bool("fix") {
|
||||||
|
return []string{fmt.Sprintf("regenerated hooks for %s", repo.FullName())}, repository.CreateDelegateHooks(repo.RepoPath())
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func runDoctorPRMergeBase(ctx *cli.Context) ([]string, error) {
|
||||||
|
numRepos := 0
|
||||||
|
numPRs := 0
|
||||||
|
numPRsUpdated := 0
|
||||||
|
results, err := iterateRepositories(func(repo *models.Repository) ([]string, error) {
|
||||||
|
numRepos++
|
||||||
|
return iteratePRs(repo, func(repo *models.Repository, pr *models.PullRequest) ([]string, error) {
|
||||||
|
numPRs++
|
||||||
|
results := []string{}
|
||||||
|
pr.BaseRepo = repo
|
||||||
|
repoPath := repo.RepoPath()
|
||||||
|
|
||||||
|
oldMergeBase := pr.MergeBase
|
||||||
|
|
||||||
|
if !pr.HasMerged {
|
||||||
|
var err error
|
||||||
|
pr.MergeBase, err = git.NewCommand("merge-base", "--", pr.BaseBranch, pr.GetGitRefName()).RunInDir(repoPath)
|
||||||
|
if err != nil {
|
||||||
|
var err2 error
|
||||||
|
pr.MergeBase, err2 = git.NewCommand("rev-parse", git.BranchPrefix+pr.BaseBranch).RunInDir(repoPath)
|
||||||
|
if err2 != nil {
|
||||||
|
results = append(results, fmt.Sprintf("WARN: Unable to get merge base for PR ID %d, #%d onto %s in %s/%s", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name))
|
||||||
|
log.Error("Unable to get merge base for PR ID %d, Index %d in %s/%s. Error: %v & %v", pr.ID, pr.Index, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err, err2)
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parentsString, err := git.NewCommand("rev-list", "--parents", "-n", "1", pr.MergedCommitID).RunInDir(repoPath)
|
||||||
|
if err != nil {
|
||||||
|
results = append(results, fmt.Sprintf("WARN: Unable to get parents for merged PR ID %d, #%d onto %s in %s/%s", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name))
|
||||||
|
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err)
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
parents := strings.Split(strings.TrimSpace(parentsString), " ")
|
||||||
|
if len(parents) < 2 {
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
args := append([]string{"merge-base", "--"}, parents[1:]...)
|
||||||
|
args = append(args, pr.GetGitRefName())
|
||||||
|
|
||||||
|
pr.MergeBase, err = git.NewCommand(args...).RunInDir(repoPath)
|
||||||
|
if err != nil {
|
||||||
|
results = append(results, fmt.Sprintf("WARN: Unable to get merge base for merged PR ID %d, #%d onto %s in %s/%s", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name))
|
||||||
|
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err)
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pr.MergeBase = strings.TrimSpace(pr.MergeBase)
|
||||||
|
if pr.MergeBase != oldMergeBase {
|
||||||
|
if ctx.Bool("fix") {
|
||||||
|
if err := pr.UpdateCols("merge_base"); err != nil {
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
results = append(results, fmt.Sprintf("#%d onto %s in %s/%s: MergeBase should be %s but is %s", pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, oldMergeBase, pr.MergeBase))
|
||||||
|
}
|
||||||
|
numPRsUpdated++
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if ctx.Bool("fix") {
|
||||||
|
results = append(results, fmt.Sprintf("%d PR mergebases updated of %d PRs total in %d repos", numPRsUpdated, numPRs, numRepos))
|
||||||
|
} else {
|
||||||
|
if numPRsUpdated > 0 && err == nil {
|
||||||
|
return results, fmt.Errorf("%d PRs with incorrect mergebases of %d PRs total in %d repos", numPRsUpdated, numPRs, numRepos)
|
||||||
|
}
|
||||||
|
results = append(results, fmt.Sprintf("%d PRs with incorrect mergebases of %d PRs total in %d repos", numPRsUpdated, numPRs, numRepos))
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func runDoctorScriptType(ctx *cli.Context) ([]string, error) {
|
||||||
|
path, err := exec.LookPath(setting.ScriptType)
|
||||||
|
if err != nil {
|
||||||
|
return []string{fmt.Sprintf("ScriptType %s is not on the current PATH", setting.ScriptType)}, err
|
||||||
|
}
|
||||||
|
return []string{fmt.Sprintf("ScriptType %s is on the current PATH at %s", setting.ScriptType, path)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runDoctorCheckDBConsistency(ctx *cli.Context) ([]string, error) {
|
||||||
|
var results []string
|
||||||
|
|
||||||
|
// make sure DB version is uptodate
|
||||||
|
if err := models.NewEngine(context.Background(), migrations.EnsureUpToDate); err != nil {
|
||||||
|
return nil, fmt.Errorf("model version on the database does not match the current Gitea version. Model consistency will not be checked until the database is upgraded")
|
||||||
|
}
|
||||||
|
|
||||||
|
//find labels without existing repo or org
|
||||||
|
count, err := models.CountOrphanedLabels()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
if ctx.Bool("fix") {
|
||||||
|
if err = models.DeleteOrphanedLabels(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results = append(results, fmt.Sprintf("%d labels without existing repository/organisation deleted", count))
|
||||||
|
} else {
|
||||||
|
results = append(results, fmt.Sprintf("%d labels without existing repository/organisation", count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//find issues without existing repository
|
||||||
|
count, err = models.CountOrphanedIssues()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
if ctx.Bool("fix") {
|
||||||
|
if err = models.DeleteOrphanedIssues(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results = append(results, fmt.Sprintf("%d issues without existing repository deleted", count))
|
||||||
|
} else {
|
||||||
|
results = append(results, fmt.Sprintf("%d issues without existing repository", count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//find pulls without existing issues
|
||||||
|
count, err = models.CountOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
if ctx.Bool("fix") {
|
||||||
|
if err = models.DeleteOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results = append(results, fmt.Sprintf("%d pull requests without existing issue deleted", count))
|
||||||
|
} else {
|
||||||
|
results = append(results, fmt.Sprintf("%d pull requests without existing issue", count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//find tracked times without existing issues/pulls
|
||||||
|
count, err = models.CountOrphanedObjects("tracked_time", "issue", "tracked_time.issue_id=issue.id")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
if ctx.Bool("fix") {
|
||||||
|
if err = models.DeleteOrphanedObjects("tracked_time", "issue", "tracked_time.issue_id=issue.id"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results = append(results, fmt.Sprintf("%d tracked times without existing issue deleted", count))
|
||||||
|
} else {
|
||||||
|
results = append(results, fmt.Sprintf("%d tracked times without existing issue", count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err = models.CountNullArchivedRepository()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if count > 0 {
|
||||||
|
if ctx.Bool("fix") {
|
||||||
|
updatedCount, err := models.FixNullArchivedRepository()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results = append(results, fmt.Sprintf("%d repositories with null is_archived updated", updatedCount))
|
||||||
|
} else {
|
||||||
|
results = append(results, fmt.Sprintf("%d repositories with null is_archived", count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//ToDo: function to recalc all counters
|
||||||
|
|
||||||
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|||||||
360
cmd/dump.go
360
cmd/dump.go
@@ -7,89 +7,21 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/json"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/storage"
|
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
|
|
||||||
"gitea.com/go-chi/session"
|
"github.com/unknwon/cae/zip"
|
||||||
archiver "github.com/mholt/archiver/v3"
|
"github.com/unknwon/com"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
func addFile(w archiver.Writer, filePath, absPath string, verbose bool) error {
|
|
||||||
if verbose {
|
|
||||||
log.Info("Adding file %s\n", filePath)
|
|
||||||
}
|
|
||||||
file, err := os.Open(absPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
fileInfo, err := file.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return w.Write(archiver.File{
|
|
||||||
FileInfo: archiver.FileInfo{
|
|
||||||
FileInfo: fileInfo,
|
|
||||||
CustomName: filePath,
|
|
||||||
},
|
|
||||||
ReadCloser: file,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func isSubdir(upper, lower string) (bool, error) {
|
|
||||||
if relPath, err := filepath.Rel(upper, lower); err != nil {
|
|
||||||
return false, err
|
|
||||||
} else if relPath == "." || !strings.HasPrefix(relPath, ".") {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type outputType struct {
|
|
||||||
Enum []string
|
|
||||||
Default string
|
|
||||||
selected string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o outputType) Join() string {
|
|
||||||
return strings.Join(o.Enum, ", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *outputType) Set(value string) error {
|
|
||||||
for _, enum := range o.Enum {
|
|
||||||
if enum == value {
|
|
||||||
o.selected = value
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("allowed values are %s", o.Join())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o outputType) String() string {
|
|
||||||
if o.selected == "" {
|
|
||||||
return o.Default
|
|
||||||
}
|
|
||||||
return o.selected
|
|
||||||
}
|
|
||||||
|
|
||||||
var outputTypeEnum = &outputType{
|
|
||||||
Enum: []string{"zip", "rar", "tar", "sz", "tar.gz", "tar.xz", "tar.bz2", "tar.br", "tar.lz4"},
|
|
||||||
Default: "zip",
|
|
||||||
}
|
|
||||||
|
|
||||||
// CmdDump represents the available dump sub-command.
|
// CmdDump represents the available dump sub-command.
|
||||||
var CmdDump = cli.Command{
|
var CmdDump = cli.Command{
|
||||||
Name: "dump",
|
Name: "dump",
|
||||||
@@ -101,7 +33,7 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
|
|||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "file, f",
|
Name: "file, f",
|
||||||
Value: fmt.Sprintf("gitea-dump-%d.zip", time.Now().Unix()),
|
Value: fmt.Sprintf("gitea-dump-%d.zip", time.Now().Unix()),
|
||||||
Usage: "Name of the dump file which will be created. Supply '-' for stdout. See type for available types.",
|
Usage: "Name of the dump file which will be created.",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "verbose, V",
|
Name: "verbose, V",
|
||||||
@@ -124,23 +56,6 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
|
|||||||
Name: "skip-log, L",
|
Name: "skip-log, L",
|
||||||
Usage: "Skip the log dumping",
|
Usage: "Skip the log dumping",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "skip-custom-dir",
|
|
||||||
Usage: "Skip custom directory",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "skip-lfs-data",
|
|
||||||
Usage: "Skip LFS data",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "skip-attachment-data",
|
|
||||||
Usage: "Skip attachment data",
|
|
||||||
},
|
|
||||||
cli.GenericFlag{
|
|
||||||
Name: "type",
|
|
||||||
Value: outputTypeEnum,
|
|
||||||
Usage: fmt.Sprintf("Dump output format: %s", outputTypeEnum.Join()),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,118 +65,52 @@ func fatal(format string, args ...interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runDump(ctx *cli.Context) error {
|
func runDump(ctx *cli.Context) error {
|
||||||
var file *os.File
|
setting.NewContext()
|
||||||
fileName := ctx.String("file")
|
|
||||||
outType := ctx.String("type")
|
|
||||||
if fileName == "-" {
|
|
||||||
file = os.Stdout
|
|
||||||
err := log.DelLogger("console")
|
|
||||||
if err != nil {
|
|
||||||
fatal("Deleting default logger failed. Can not write to stdout: %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fileName = strings.TrimSuffix(fileName, path.Ext(fileName))
|
|
||||||
fileName += "." + outType
|
|
||||||
}
|
|
||||||
setting.LoadFromExisting()
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
if _, err := setting.Cfg.Section("log.console").NewKey("STDERR", "true"); err != nil {
|
|
||||||
fatal("Setting console logger to stderr failed: %v", err)
|
|
||||||
}
|
|
||||||
if !setting.InstallLock {
|
|
||||||
log.Error("Is '%s' really the right config path?\n", setting.CustomConf)
|
|
||||||
return fmt.Errorf("gitea is not initialized")
|
|
||||||
}
|
|
||||||
setting.NewServices() // cannot access session settings otherwise
|
setting.NewServices() // cannot access session settings otherwise
|
||||||
|
|
||||||
stdCtx, cancel := installSignals()
|
err := models.SetEngine()
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
err := db.InitEngine(stdCtx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := storage.Init(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if file == nil {
|
|
||||||
file, err = os.Create(fileName)
|
|
||||||
if err != nil {
|
|
||||||
fatal("Unable to open %s: %v", fileName, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
absFileName, err := filepath.Abs(fileName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
verbose := ctx.Bool("verbose")
|
|
||||||
var iface interface{}
|
|
||||||
if fileName == "-" {
|
|
||||||
iface, err = archiver.ByExtension(fmt.Sprintf(".%s", outType))
|
|
||||||
} else {
|
|
||||||
iface, err = archiver.ByExtension(fileName)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
fatal("Unable to get archiver for extension: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
w, _ := iface.(archiver.Writer)
|
|
||||||
if err := w.Create(file); err != nil {
|
|
||||||
fatal("Creating archiver.Writer failed: %v", err)
|
|
||||||
}
|
|
||||||
defer w.Close()
|
|
||||||
|
|
||||||
if ctx.IsSet("skip-repository") && ctx.Bool("skip-repository") {
|
|
||||||
log.Info("Skip dumping local repositories")
|
|
||||||
} else {
|
|
||||||
log.Info("Dumping local repositories... %s", setting.RepoRootPath)
|
|
||||||
if err := addRecursiveExclude(w, "repos", setting.RepoRootPath, []string{absFileName}, verbose); err != nil {
|
|
||||||
fatal("Failed to include repositories: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.IsSet("skip-lfs-data") && ctx.Bool("skip-lfs-data") {
|
|
||||||
log.Info("Skip dumping LFS data")
|
|
||||||
} else if err := storage.LFS.IterateObjects(func(objPath string, object storage.Object) error {
|
|
||||||
info, err := object.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpDir := ctx.String("tempdir")
|
tmpDir := ctx.String("tempdir")
|
||||||
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
||||||
fatal("Path does not exist: %s", tmpDir)
|
fatal("Path does not exist: %s", tmpDir)
|
||||||
}
|
}
|
||||||
|
tmpWorkDir, err := ioutil.TempDir(tmpDir, "gitea-dump-")
|
||||||
dbDump, err := os.CreateTemp(tmpDir, "gitea-db.sql")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal("Failed to create tmp file: %v", err)
|
fatal("Failed to create tmp work directory: %v", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
log.Info("Creating tmp work dir: %s", tmpWorkDir)
|
||||||
if err := util.Remove(dbDump.Name()); err != nil {
|
|
||||||
log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err)
|
// work-around #1103
|
||||||
|
if os.Getenv("TMPDIR") == "" {
|
||||||
|
os.Setenv("TMPDIR", tmpWorkDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
dbDump := path.Join(tmpWorkDir, "gitea-db.sql")
|
||||||
|
|
||||||
|
fileName := ctx.String("file")
|
||||||
|
log.Info("Packing dump files...")
|
||||||
|
z, err := zip.Create(fileName)
|
||||||
|
if err != nil {
|
||||||
|
fatal("Failed to create %s: %v", fileName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
zip.Verbose = ctx.Bool("verbose")
|
||||||
|
|
||||||
|
if ctx.IsSet("skip-repository") && ctx.Bool("skip-repository") {
|
||||||
|
log.Info("Skip dumping local repositories")
|
||||||
|
} else {
|
||||||
|
log.Info("Dumping local repositories...%s", setting.RepoRootPath)
|
||||||
|
reposDump := path.Join(tmpWorkDir, "gitea-repo.zip")
|
||||||
|
if err := zip.PackTo(setting.RepoRootPath, reposDump, true); err != nil {
|
||||||
|
fatal("Failed to dump local repositories: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
if err := z.AddFile("gitea-repo.zip", reposDump); err != nil {
|
||||||
|
fatal("Failed to include gitea-repo.zip: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
targetDBType := ctx.String("database")
|
targetDBType := ctx.String("database")
|
||||||
if len(targetDBType) > 0 && targetDBType != setting.Database.Type {
|
if len(targetDBType) > 0 && targetDBType != setting.Database.Type {
|
||||||
@@ -270,131 +119,74 @@ func runDump(ctx *cli.Context) error {
|
|||||||
log.Info("Dumping database...")
|
log.Info("Dumping database...")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := db.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
|
if err := models.DumpDatabase(dbDump, targetDBType); err != nil {
|
||||||
fatal("Failed to dump database: %v", err)
|
fatal("Failed to dump database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := addFile(w, "gitea-db.sql", dbDump.Name(), verbose); err != nil {
|
if err := z.AddFile("gitea-db.sql", dbDump); err != nil {
|
||||||
fatal("Failed to include gitea-db.sql: %v", err)
|
fatal("Failed to include gitea-db.sql: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(setting.CustomConf) > 0 {
|
if len(setting.CustomConf) > 0 {
|
||||||
log.Info("Adding custom configuration file from %s", setting.CustomConf)
|
log.Info("Adding custom configuration file from %s", setting.CustomConf)
|
||||||
if err := addFile(w, "app.ini", setting.CustomConf, verbose); err != nil {
|
if err := z.AddFile("app.ini", setting.CustomConf); err != nil {
|
||||||
fatal("Failed to include specified app.ini: %v", err)
|
fatal("Failed to include specified app.ini: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.IsSet("skip-custom-dir") && ctx.Bool("skip-custom-dir") {
|
customDir, err := os.Stat(setting.CustomPath)
|
||||||
log.Info("Skipping custom directory")
|
if err == nil && customDir.IsDir() {
|
||||||
} else {
|
if err := z.AddDir("custom", setting.CustomPath); err != nil {
|
||||||
customDir, err := os.Stat(setting.CustomPath)
|
fatal("Failed to include custom: %v", err)
|
||||||
if err == nil && customDir.IsDir() {
|
|
||||||
if is, _ := isSubdir(setting.AppDataPath, setting.CustomPath); !is {
|
|
||||||
if err := addRecursiveExclude(w, "custom", setting.CustomPath, []string{absFileName}, verbose); err != nil {
|
|
||||||
fatal("Failed to include custom: %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Info("Custom dir %s is inside data dir %s, skipped", setting.CustomPath, setting.AppDataPath)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Info("Custom dir %s doesn't exist, skipped", setting.CustomPath)
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log.Info("Custom dir %s doesn't exist, skipped", setting.CustomPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
isExist, err := util.IsExist(setting.AppDataPath)
|
if com.IsExist(setting.AppDataPath) {
|
||||||
if err != nil {
|
|
||||||
log.Error("Unable to check if %s exists. Error: %v", setting.AppDataPath, err)
|
|
||||||
}
|
|
||||||
if isExist {
|
|
||||||
log.Info("Packing data directory...%s", setting.AppDataPath)
|
log.Info("Packing data directory...%s", setting.AppDataPath)
|
||||||
|
|
||||||
var excludes []string
|
var sessionAbsPath string
|
||||||
if setting.Cfg.Section("session").Key("PROVIDER").Value() == "file" {
|
if setting.SessionConfig.Provider == "file" {
|
||||||
var opts session.Options
|
sessionAbsPath = setting.SessionConfig.ProviderConfig
|
||||||
if err = json.Unmarshal([]byte(setting.SessionConfig.ProviderConfig), &opts); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
excludes = append(excludes, opts.ProviderConfig)
|
|
||||||
}
|
}
|
||||||
|
if err := zipAddDirectoryExclude(z, "data", setting.AppDataPath, sessionAbsPath); err != nil {
|
||||||
excludes = append(excludes, setting.RepoRootPath)
|
|
||||||
excludes = append(excludes, setting.LFS.Path)
|
|
||||||
excludes = append(excludes, setting.Attachment.Path)
|
|
||||||
excludes = append(excludes, setting.LogRootPath)
|
|
||||||
excludes = append(excludes, absFileName)
|
|
||||||
if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil {
|
|
||||||
fatal("Failed to include data directory: %v", err)
|
fatal("Failed to include data directory: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.IsSet("skip-attachment-data") && ctx.Bool("skip-attachment-data") {
|
|
||||||
log.Info("Skip dumping attachment data")
|
|
||||||
} else if err := storage.Attachments.IterateObjects(func(objPath string, object storage.Object) error {
|
|
||||||
info, err := object.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Doesn't check if LogRootPath exists before processing --skip-log intentionally,
|
// 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
|
// ensuring that it's clear the dump is skipped whether the directory's initialized
|
||||||
// yet or not.
|
// yet or not.
|
||||||
if ctx.IsSet("skip-log") && ctx.Bool("skip-log") {
|
if ctx.IsSet("skip-log") && ctx.Bool("skip-log") {
|
||||||
log.Info("Skip dumping log files")
|
log.Info("Skip dumping log files")
|
||||||
} else {
|
} else if com.IsExist(setting.LogRootPath) {
|
||||||
isExist, err := util.IsExist(setting.LogRootPath)
|
if err := z.AddDir("log", setting.LogRootPath); err != nil {
|
||||||
if err != nil {
|
fatal("Failed to include log: %v", err)
|
||||||
log.Error("Unable to check if %s exists. Error: %v", setting.LogRootPath, err)
|
|
||||||
}
|
|
||||||
if isExist {
|
|
||||||
if err := addRecursiveExclude(w, "log", setting.LogRootPath, []string{absFileName}, verbose); err != nil {
|
|
||||||
fatal("Failed to include log: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if fileName != "-" {
|
if err = z.Close(); err != nil {
|
||||||
if err = w.Close(); err != nil {
|
_ = os.Remove(fileName)
|
||||||
_ = util.Remove(fileName)
|
fatal("Failed to save %s: %v", fileName, err)
|
||||||
fatal("Failed to save %s: %v", fileName, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.Chmod(fileName, 0600); err != nil {
|
|
||||||
log.Info("Can't change file access permissions mask to 0600: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if fileName != "-" {
|
if err := os.Chmod(fileName, 0600); err != nil {
|
||||||
log.Info("Finish dumping in file %s", fileName)
|
log.Info("Can't change file access permissions mask to 0600: %v", err)
|
||||||
} else {
|
|
||||||
log.Info("Finish dumping to stdout")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Info("Removing tmp work dir: %s", tmpWorkDir)
|
||||||
|
|
||||||
|
if err := os.RemoveAll(tmpWorkDir); err != nil {
|
||||||
|
fatal("Failed to remove %s: %v", tmpWorkDir, err)
|
||||||
|
}
|
||||||
|
log.Info("Finish dumping in file %s", fileName)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func contains(slice []string, s string) bool {
|
// zipAddDirectoryExclude zips absPath to specified zipPath inside z excluding excludeAbsPath
|
||||||
for _, v := range slice {
|
func zipAddDirectoryExclude(zip *zip.ZipArchive, zipPath, absPath string, excludeAbsPath string) error {
|
||||||
if v == s {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// addRecursiveExclude zips absPath to specified insidePath inside writer excluding excludeAbsPath
|
|
||||||
func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeAbsPath []string, verbose bool) error {
|
|
||||||
absPath, err := filepath.Abs(absPath)
|
absPath, err := filepath.Abs(absPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -405,24 +197,24 @@ func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeA
|
|||||||
}
|
}
|
||||||
defer dir.Close()
|
defer dir.Close()
|
||||||
|
|
||||||
|
zip.AddEmptyDir(zipPath)
|
||||||
|
|
||||||
files, err := dir.Readdir(0)
|
files, err := dir.Readdir(0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
currentAbsPath := path.Join(absPath, file.Name())
|
currentAbsPath := path.Join(absPath, file.Name())
|
||||||
currentInsidePath := path.Join(insidePath, file.Name())
|
currentZipPath := path.Join(zipPath, file.Name())
|
||||||
if file.IsDir() {
|
if file.IsDir() {
|
||||||
if !contains(excludeAbsPath, currentAbsPath) {
|
if currentAbsPath != excludeAbsPath {
|
||||||
if err := addFile(w, currentInsidePath, currentAbsPath, false); err != nil {
|
if err = zipAddDirectoryExclude(zip, currentZipPath, currentAbsPath, excludeAbsPath); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = addRecursiveExclude(w, currentInsidePath, currentAbsPath, excludeAbsPath, verbose); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if err = addFile(w, currentInsidePath, currentAbsPath, verbose); err != nil {
|
if err = zip.AddFile(currentZipPath, currentAbsPath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
165
cmd/dump_repo.go
165
cmd/dump_repo.go
@@ -1,165 +0,0 @@
|
|||||||
// 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 (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/convert"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
base "code.gitea.io/gitea/modules/migration"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
"code.gitea.io/gitea/modules/structs"
|
|
||||||
"code.gitea.io/gitea/services/migrations"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CmdDumpRepository represents the available dump repository sub-command.
|
|
||||||
var CmdDumpRepository = cli.Command{
|
|
||||||
Name: "dump-repo",
|
|
||||||
Usage: "Dump the repository from git/github/gitea/gitlab",
|
|
||||||
Description: "This is a command for dumping the repository data.",
|
|
||||||
Action: runDumpRepository,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "git_service",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Git service, git, github, gitea, gitlab. If clone_addr could be recognized, this could be ignored.",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "repo_dir, r",
|
|
||||||
Value: "./data",
|
|
||||||
Usage: "Repository dir path to store the data",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "clone_addr",
|
|
||||||
Value: "",
|
|
||||||
Usage: "The URL will be clone, currently could be a git/github/gitea/gitlab http/https URL",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "auth_username",
|
|
||||||
Value: "",
|
|
||||||
Usage: "The username to visit the clone_addr",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "auth_password",
|
|
||||||
Value: "",
|
|
||||||
Usage: "The password to visit the clone_addr",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "auth_token",
|
|
||||||
Value: "",
|
|
||||||
Usage: "The personal token to visit the clone_addr",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "owner_name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "The data will be stored on a directory with owner name if not empty",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "repo_name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "The data will be stored on a directory with repository name if not empty",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "units",
|
|
||||||
Value: "",
|
|
||||||
Usage: `Which items will be migrated, 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.`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func runDumpRepository(ctx *cli.Context) error {
|
|
||||||
stdCtx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(stdCtx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("AppPath: %s", setting.AppPath)
|
|
||||||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
|
||||||
log.Info("Custom path: %s", setting.CustomPath)
|
|
||||||
log.Info("Log path: %s", setting.LogRootPath)
|
|
||||||
log.Info("Configuration file: %s", setting.CustomConf)
|
|
||||||
|
|
||||||
var (
|
|
||||||
serviceType structs.GitServiceType
|
|
||||||
cloneAddr = ctx.String("clone_addr")
|
|
||||||
serviceStr = ctx.String("git_service")
|
|
||||||
)
|
|
||||||
|
|
||||||
if strings.HasPrefix(strings.ToLower(cloneAddr), "https://github.com/") {
|
|
||||||
serviceStr = "github"
|
|
||||||
} else if strings.HasPrefix(strings.ToLower(cloneAddr), "https://gitlab.com/") {
|
|
||||||
serviceStr = "gitlab"
|
|
||||||
} else if strings.HasPrefix(strings.ToLower(cloneAddr), "https://gitea.com/") {
|
|
||||||
serviceStr = "gitea"
|
|
||||||
}
|
|
||||||
if serviceStr == "" {
|
|
||||||
return errors.New("git_service missed or clone_addr cannot be recognized")
|
|
||||||
}
|
|
||||||
serviceType = convert.ToGitServiceType(serviceStr)
|
|
||||||
|
|
||||||
var opts = base.MigrateOptions{
|
|
||||||
GitServiceType: serviceType,
|
|
||||||
CloneAddr: cloneAddr,
|
|
||||||
AuthUsername: ctx.String("auth_username"),
|
|
||||||
AuthPassword: ctx.String("auth_password"),
|
|
||||||
AuthToken: ctx.String("auth_token"),
|
|
||||||
RepoName: ctx.String("repo_name"),
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ctx.String("units")) == 0 {
|
|
||||||
opts.Wiki = true
|
|
||||||
opts.Issues = true
|
|
||||||
opts.Milestones = true
|
|
||||||
opts.Labels = true
|
|
||||||
opts.Releases = true
|
|
||||||
opts.Comments = true
|
|
||||||
opts.PullRequests = true
|
|
||||||
opts.ReleaseAssets = true
|
|
||||||
} else {
|
|
||||||
units := strings.Split(ctx.String("units"), ",")
|
|
||||||
for _, unit := range units {
|
|
||||||
switch strings.ToLower(unit) {
|
|
||||||
case "wiki":
|
|
||||||
opts.Wiki = true
|
|
||||||
case "issues":
|
|
||||||
opts.Issues = true
|
|
||||||
case "milestones":
|
|
||||||
opts.Milestones = true
|
|
||||||
case "labels":
|
|
||||||
opts.Labels = true
|
|
||||||
case "releases":
|
|
||||||
opts.Releases = true
|
|
||||||
case "release_assets":
|
|
||||||
opts.ReleaseAssets = true
|
|
||||||
case "comments":
|
|
||||||
opts.Comments = true
|
|
||||||
case "pull_requests":
|
|
||||||
opts.PullRequests = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := migrations.DumpRepository(
|
|
||||||
context.Background(),
|
|
||||||
ctx.String("repo_dir"),
|
|
||||||
ctx.String("owner_name"),
|
|
||||||
opts,
|
|
||||||
); err != nil {
|
|
||||||
log.Fatal("Failed to dump repository: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Trace("Dump finished!!!")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build bindata
|
|
||||||
// +build bindata
|
// +build bindata
|
||||||
|
|
||||||
package cmd
|
package cmd
|
||||||
@@ -20,7 +19,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/public"
|
"code.gitea.io/gitea/modules/public"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/templates"
|
"code.gitea.io/gitea/modules/templates"
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
|
|
||||||
"github.com/gobwas/glob"
|
"github.com/gobwas/glob"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
@@ -115,7 +113,7 @@ func initEmbeddedExtractor(c *cli.Context) error {
|
|||||||
log.DelNamedLogger(log.DEFAULT)
|
log.DelNamedLogger(log.DEFAULT)
|
||||||
|
|
||||||
// Read configuration file
|
// Read configuration file
|
||||||
setting.LoadAllowEmpty()
|
setting.NewContext()
|
||||||
|
|
||||||
pats, err := getPatterns(c.Args())
|
pats, err := getPatterns(c.Args())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -273,7 +271,7 @@ func extractAsset(d string, a asset, overwrite, rename bool) error {
|
|||||||
} else if !fi.Mode().IsRegular() {
|
} else if !fi.Mode().IsRegular() {
|
||||||
return fmt.Errorf("%s already exists, but it's not a regular file", dest)
|
return fmt.Errorf("%s already exists, but it's not a regular file", dest)
|
||||||
} else if rename {
|
} else if rename {
|
||||||
if err := util.Rename(dest, dest+".bak"); err != nil {
|
if err := os.Rename(dest, dest+".bak"); err != nil {
|
||||||
return fmt.Errorf("Error creating backup for %s: %v", 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)
|
// Attempt to respect file permissions mask (even if user:group will be set anew)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build !bindata
|
|
||||||
// +build !bindata
|
// +build !bindata
|
||||||
|
|
||||||
package cmd
|
package cmd
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ func runGenerateInternalToken(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runGenerateLfsJwtSecret(c *cli.Context) error {
|
func runGenerateLfsJwtSecret(c *cli.Context) error {
|
||||||
JWTSecretBase64, err := generate.NewJwtSecretBase64()
|
JWTSecretBase64, err := generate.NewJwtSecret()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
449
cmd/hook.go
449
cmd/hook.go
@@ -38,7 +38,6 @@ var (
|
|||||||
subcmdHookPreReceive,
|
subcmdHookPreReceive,
|
||||||
subcmdHookUpdate,
|
subcmdHookUpdate,
|
||||||
subcmdHookPostReceive,
|
subcmdHookPostReceive,
|
||||||
subcmdHookProcReceive,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,45 +46,18 @@ var (
|
|||||||
Usage: "Delegate pre-receive Git hook",
|
Usage: "Delegate pre-receive Git hook",
|
||||||
Description: "This command should only be called by Git",
|
Description: "This command should only be called by Git",
|
||||||
Action: runHookPreReceive,
|
Action: runHookPreReceive,
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "debug",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
subcmdHookUpdate = cli.Command{
|
subcmdHookUpdate = cli.Command{
|
||||||
Name: "update",
|
Name: "update",
|
||||||
Usage: "Delegate update Git hook",
|
Usage: "Delegate update Git hook",
|
||||||
Description: "This command should only be called by Git",
|
Description: "This command should only be called by Git",
|
||||||
Action: runHookUpdate,
|
Action: runHookUpdate,
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "debug",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
subcmdHookPostReceive = cli.Command{
|
subcmdHookPostReceive = cli.Command{
|
||||||
Name: "post-receive",
|
Name: "post-receive",
|
||||||
Usage: "Delegate post-receive Git hook",
|
Usage: "Delegate post-receive Git hook",
|
||||||
Description: "This command should only be called by Git",
|
Description: "This command should only be called by Git",
|
||||||
Action: runHookPostReceive,
|
Action: runHookPostReceive,
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "debug",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
// 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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -165,26 +137,25 @@ func runHookPreReceive(c *cli.Context) error {
|
|||||||
if os.Getenv(models.EnvIsInternal) == "true" {
|
if os.Getenv(models.EnvIsInternal) == "true" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
setup("hooks/pre-receive.log", c.Bool("debug"))
|
setup("hooks/pre-receive.log", false)
|
||||||
|
|
||||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||||
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
||||||
return fail(`Rejecting changes as Gitea environment not set.
|
fail(`Rejecting changes as Gitea environment not set.
|
||||||
If you are pushing over SSH you must push with a key managed by
|
If you are pushing over SSH you must push with a key managed by
|
||||||
Gitea or set your environment appropriately.`, "")
|
Gitea or set your environment appropriately.`, "")
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// the environment is set by serv command
|
// the environment setted on serv command
|
||||||
isWiki := os.Getenv(models.EnvRepoIsWiki) == "true"
|
isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
|
||||||
username := os.Getenv(models.EnvRepoUsername)
|
username := os.Getenv(models.EnvRepoUsername)
|
||||||
reponame := os.Getenv(models.EnvRepoName)
|
reponame := os.Getenv(models.EnvRepoName)
|
||||||
userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||||
prID, _ := strconv.ParseInt(os.Getenv(models.EnvPRID), 10, 64)
|
prID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchPRID), 10, 64)
|
||||||
isDeployKey, _ := strconv.ParseBool(os.Getenv(models.EnvIsDeployKey))
|
isDeployKey, _ := strconv.ParseBool(os.Getenv(models.EnvIsDeployKey))
|
||||||
|
|
||||||
hookOptions := private.HookOptions{
|
hookOptions := private.HookOptions{
|
||||||
@@ -192,8 +163,7 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
|
GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
|
||||||
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
||||||
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
||||||
GitPushOptions: pushOptions(),
|
ProtectedBranchID: prID,
|
||||||
PullRequestID: prID,
|
|
||||||
IsDeployKey: isDeployKey,
|
IsDeployKey: isDeployKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,11 +188,6 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
supportProcRecive := false
|
|
||||||
if git.CheckGitVersionAtLeast("2.29") == nil {
|
|
||||||
supportProcRecive = true
|
|
||||||
}
|
|
||||||
|
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
// TODO: support news feeds for wiki
|
// TODO: support news feeds for wiki
|
||||||
if isWiki {
|
if isWiki {
|
||||||
@@ -240,10 +205,8 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
total++
|
total++
|
||||||
lastline++
|
lastline++
|
||||||
|
|
||||||
// If the ref is a branch or tag, check if it's protected
|
// If the ref is a branch, check if it's protected
|
||||||
// if supportProcRecive all ref should be checked because
|
if strings.HasPrefix(refFullName, git.BranchPrefix) {
|
||||||
// permission check was delayed
|
|
||||||
if supportProcRecive || strings.HasPrefix(refFullName, git.BranchPrefix) || strings.HasPrefix(refFullName, git.TagPrefix) {
|
|
||||||
oldCommitIDs[count] = oldCommitID
|
oldCommitIDs[count] = oldCommitID
|
||||||
newCommitIDs[count] = newCommitID
|
newCommitIDs[count] = newCommitID
|
||||||
refFullNames[count] = refFullName
|
refFullNames[count] = refFullName
|
||||||
@@ -251,19 +214,19 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
fmt.Fprintf(out, "*")
|
fmt.Fprintf(out, "*")
|
||||||
|
|
||||||
if count >= hookBatchSize {
|
if count >= hookBatchSize {
|
||||||
fmt.Fprintf(out, " Checking %d references\n", count)
|
fmt.Fprintf(out, " Checking %d branches\n", count)
|
||||||
|
|
||||||
hookOptions.OldCommitIDs = oldCommitIDs
|
hookOptions.OldCommitIDs = oldCommitIDs
|
||||||
hookOptions.NewCommitIDs = newCommitIDs
|
hookOptions.NewCommitIDs = newCommitIDs
|
||||||
hookOptions.RefFullNames = refFullNames
|
hookOptions.RefFullNames = refFullNames
|
||||||
statusCode, msg := private.HookPreReceive(ctx, username, reponame, hookOptions)
|
statusCode, msg := private.HookPreReceive(username, reponame, hookOptions)
|
||||||
switch statusCode {
|
switch statusCode {
|
||||||
case http.StatusOK:
|
case http.StatusOK:
|
||||||
// no-op
|
// no-op
|
||||||
case http.StatusInternalServerError:
|
case http.StatusInternalServerError:
|
||||||
return fail("Internal Server Error", msg)
|
fail("Internal Server Error", msg)
|
||||||
default:
|
default:
|
||||||
return fail(msg, "")
|
fail(msg, "")
|
||||||
}
|
}
|
||||||
count = 0
|
count = 0
|
||||||
lastline = 0
|
lastline = 0
|
||||||
@@ -282,17 +245,18 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
hookOptions.NewCommitIDs = newCommitIDs[:count]
|
hookOptions.NewCommitIDs = newCommitIDs[:count]
|
||||||
hookOptions.RefFullNames = refFullNames[:count]
|
hookOptions.RefFullNames = refFullNames[:count]
|
||||||
|
|
||||||
fmt.Fprintf(out, " Checking %d references\n", count)
|
fmt.Fprintf(out, " Checking %d branches\n", count)
|
||||||
|
|
||||||
statusCode, msg := private.HookPreReceive(ctx, username, reponame, hookOptions)
|
statusCode, msg := private.HookPreReceive(username, reponame, hookOptions)
|
||||||
switch statusCode {
|
switch statusCode {
|
||||||
case http.StatusInternalServerError:
|
case http.StatusInternalServerError:
|
||||||
return fail("Internal Server Error", msg)
|
fail("Internal Server Error", msg)
|
||||||
case http.StatusForbidden:
|
case http.StatusForbidden:
|
||||||
return fail(msg, "")
|
fail(msg, "")
|
||||||
}
|
}
|
||||||
} else if lastline > 0 {
|
} else if lastline > 0 {
|
||||||
fmt.Fprintf(out, "\n")
|
fmt.Fprintf(out, "\n")
|
||||||
|
lastline = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(out, "Checked %d references in total\n", total)
|
fmt.Fprintf(out, "Checked %d references in total\n", total)
|
||||||
@@ -305,28 +269,20 @@ func runHookUpdate(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runHookPostReceive(c *cli.Context) error {
|
func runHookPostReceive(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
// First of all run update-server-info no matter what
|
|
||||||
if _, err := git.NewCommandContext(ctx, "update-server-info").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 os.Getenv(models.EnvIsInternal) == "true" {
|
if os.Getenv(models.EnvIsInternal) == "true" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
setup("hooks/post-receive.log", c.Bool("debug"))
|
setup("hooks/post-receive.log", false)
|
||||||
|
|
||||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||||
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
||||||
return fail(`Rejecting changes as Gitea environment not set.
|
fail(`Rejecting changes as Gitea environment not set.
|
||||||
If you are pushing over SSH you must push with a key managed by
|
If you are pushing over SSH you must push with a key managed by
|
||||||
Gitea or set your environment appropriately.`, "")
|
Gitea or set your environment appropriately.`, "")
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var out io.Writer
|
var out io.Writer
|
||||||
@@ -342,9 +298,9 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the environment is set by serv command
|
// the environment setted on serv command
|
||||||
repoUser := os.Getenv(models.EnvRepoUsername)
|
repoUser := os.Getenv(models.EnvRepoUsername)
|
||||||
isWiki := os.Getenv(models.EnvRepoIsWiki) == "true"
|
isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
|
||||||
repoName := os.Getenv(models.EnvRepoName)
|
repoName := os.Getenv(models.EnvRepoName)
|
||||||
pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||||
pusherName := os.Getenv(models.EnvPusherName)
|
pusherName := os.Getenv(models.EnvPusherName)
|
||||||
@@ -355,7 +311,6 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
|
GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
|
||||||
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
||||||
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
||||||
GitPushOptions: pushOptions(),
|
|
||||||
}
|
}
|
||||||
oldCommitIDs := make([]string, hookBatchSize)
|
oldCommitIDs := make([]string, hookBatchSize)
|
||||||
newCommitIDs := make([]string, hookBatchSize)
|
newCommitIDs := make([]string, hookBatchSize)
|
||||||
@@ -393,11 +348,11 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
hookOptions.OldCommitIDs = oldCommitIDs
|
hookOptions.OldCommitIDs = oldCommitIDs
|
||||||
hookOptions.NewCommitIDs = newCommitIDs
|
hookOptions.NewCommitIDs = newCommitIDs
|
||||||
hookOptions.RefFullNames = refFullNames
|
hookOptions.RefFullNames = refFullNames
|
||||||
resp, err := private.HookPostReceive(ctx, repoUser, repoName, hookOptions)
|
resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
|
||||||
if resp == nil {
|
if resp == nil {
|
||||||
_ = dWriter.Close()
|
_ = dWriter.Close()
|
||||||
hookPrintResults(results)
|
hookPrintResults(results)
|
||||||
return fail("Internal Server Error", err)
|
fail("Internal Server Error", err)
|
||||||
}
|
}
|
||||||
wasEmpty = wasEmpty || resp.RepoWasEmpty
|
wasEmpty = wasEmpty || resp.RepoWasEmpty
|
||||||
results = append(results, resp.Results...)
|
results = append(results, resp.Results...)
|
||||||
@@ -408,9 +363,9 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
if count == 0 {
|
if count == 0 {
|
||||||
if wasEmpty && masterPushed {
|
if wasEmpty && masterPushed {
|
||||||
// We need to tell the repo to reset the default branch to master
|
// We need to tell the repo to reset the default branch to master
|
||||||
err := private.SetDefaultBranch(ctx, repoUser, repoName, "master")
|
err := private.SetDefaultBranch(repoUser, repoName, "master")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
|
fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Fprintf(out, "Processed %d references in total\n", total)
|
fmt.Fprintf(out, "Processed %d references in total\n", total)
|
||||||
@@ -426,11 +381,11 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
|
|
||||||
fmt.Fprintf(out, " Processing %d references\n", count)
|
fmt.Fprintf(out, " Processing %d references\n", count)
|
||||||
|
|
||||||
resp, err := private.HookPostReceive(ctx, repoUser, repoName, hookOptions)
|
resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
|
||||||
if resp == nil {
|
if resp == nil {
|
||||||
_ = dWriter.Close()
|
_ = dWriter.Close()
|
||||||
hookPrintResults(results)
|
hookPrintResults(results)
|
||||||
return fail("Internal Server Error", err)
|
fail("Internal Server Error", err)
|
||||||
}
|
}
|
||||||
wasEmpty = wasEmpty || resp.RepoWasEmpty
|
wasEmpty = wasEmpty || resp.RepoWasEmpty
|
||||||
results = append(results, resp.Results...)
|
results = append(results, resp.Results...)
|
||||||
@@ -439,9 +394,9 @@ Gitea or set your environment appropriately.`, "")
|
|||||||
|
|
||||||
if wasEmpty && masterPushed {
|
if wasEmpty && masterPushed {
|
||||||
// We need to tell the repo to reset the default branch to master
|
// We need to tell the repo to reset the default branch to master
|
||||||
err := private.SetDefaultBranch(ctx, repoUser, repoName, "master")
|
err := private.SetDefaultBranch(repoUser, repoName, "master")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
|
fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = dWriter.Close()
|
_ = dWriter.Close()
|
||||||
@@ -468,341 +423,3 @@ func hookPrintResults(results []private.HookPostReceiveBranchResult) {
|
|||||||
os.Stderr.Sync()
|
os.Stderr.Sync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pushOptions() map[string]string {
|
|
||||||
opts := make(map[string]string)
|
|
||||||
if pushCount, err := strconv.Atoi(os.Getenv(private.GitPushOptionCount)); err == nil {
|
|
||||||
for idx := 0; idx < pushCount; idx++ {
|
|
||||||
opt := os.Getenv(fmt.Sprintf("GIT_PUSH_OPTION_%d", idx))
|
|
||||||
kv := strings.SplitN(opt, "=", 2)
|
|
||||||
if len(kv) == 2 {
|
|
||||||
opts[kv[0]] = kv[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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(models.EnvRepoUsername)
|
|
||||||
repoName := os.Getenv(models.EnvRepoName)
|
|
||||||
pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
|
||||||
pusherName := os.Getenv(models.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 4 != lr {
|
|
||||||
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,41 +0,0 @@
|
|||||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
@@ -62,12 +62,9 @@ func runKeys(c *cli.Context) error {
|
|||||||
return errors.New("No key type and content provided")
|
return errors.New("No key type and content provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
setup("keys.log", false)
|
setup("keys.log", false)
|
||||||
|
|
||||||
authorizedString, err := private.AuthorizedPublicKeyByContent(ctx, content)
|
authorizedString, err := private.AuthorizedPublicKeyByContent(content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
// 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 (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/private"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
func runSendMail(c *cli.Context) error {
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
setting.LoadFromExisting()
|
|
||||||
|
|
||||||
if err := argsSet(c, "title"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
subject := c.String("title")
|
|
||||||
confirmSkiped := c.Bool("force")
|
|
||||||
body := c.String("content")
|
|
||||||
|
|
||||||
if !confirmSkiped {
|
|
||||||
if len(body) == 0 {
|
|
||||||
fmt.Print("warning: Content is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Print("Proceed with sending email? [Y/n] ")
|
|
||||||
isConfirmed, err := confirm()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else if !isConfirmed {
|
|
||||||
fmt.Println("The mail was not sent")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
status, message := private.SendEmail(ctx, subject, body, nil)
|
|
||||||
if status != http.StatusOK {
|
|
||||||
fmt.Printf("error: %s\n", message)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Success: %s\n", message)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
416
cmd/manager.go
416
cmd/manager.go
@@ -10,7 +10,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
@@ -26,27 +25,16 @@ var (
|
|||||||
subcmdShutdown,
|
subcmdShutdown,
|
||||||
subcmdRestart,
|
subcmdRestart,
|
||||||
subcmdFlushQueues,
|
subcmdFlushQueues,
|
||||||
subcmdLogging,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
subcmdShutdown = cli.Command{
|
subcmdShutdown = cli.Command{
|
||||||
Name: "shutdown",
|
Name: "shutdown",
|
||||||
Usage: "Gracefully shutdown the running process",
|
Usage: "Gracefully shutdown the running process",
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "debug",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: runShutdown,
|
Action: runShutdown,
|
||||||
}
|
}
|
||||||
subcmdRestart = cli.Command{
|
subcmdRestart = cli.Command{
|
||||||
Name: "restart",
|
Name: "restart",
|
||||||
Usage: "Gracefully restart the running process - (not implemented for windows servers)",
|
Usage: "Gracefully restart the running process - (not implemented for windows servers)",
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "debug",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: runRestart,
|
Action: runRestart,
|
||||||
}
|
}
|
||||||
subcmdFlushQueues = cli.Command{
|
subcmdFlushQueues = cli.Command{
|
||||||
@@ -58,344 +46,21 @@ var (
|
|||||||
Name: "timeout",
|
Name: "timeout",
|
||||||
Value: 60 * time.Second,
|
Value: 60 * time.Second,
|
||||||
Usage: "Timeout for the flushing process",
|
Usage: "Timeout for the flushing process",
|
||||||
}, cli.BoolFlag{
|
|
||||||
Name: "non-blocking",
|
|
||||||
Usage: "Set to true to not wait for flush to complete before returning",
|
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "debug",
|
Name: "non-blocking",
|
||||||
},
|
Usage: "Set to true to not wait for flush to complete before returning",
|
||||||
},
|
|
||||||
}
|
|
||||||
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 {
|
func runShutdown(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
setup("manager", false)
|
||||||
defer cancel()
|
statusCode, msg := private.Shutdown()
|
||||||
|
|
||||||
setup("manager", c.Bool("debug"))
|
|
||||||
statusCode, msg := private.Shutdown(ctx)
|
|
||||||
switch statusCode {
|
switch statusCode {
|
||||||
case http.StatusInternalServerError:
|
case http.StatusInternalServerError:
|
||||||
return fail("InternalServerError", msg)
|
fail("InternalServerError", msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
fmt.Fprintln(os.Stdout, msg)
|
||||||
@@ -403,14 +68,11 @@ func runShutdown(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runRestart(c *cli.Context) error {
|
func runRestart(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
setup("manager", false)
|
||||||
defer cancel()
|
statusCode, msg := private.Restart()
|
||||||
|
|
||||||
setup("manager", c.Bool("debug"))
|
|
||||||
statusCode, msg := private.Restart(ctx)
|
|
||||||
switch statusCode {
|
switch statusCode {
|
||||||
case http.StatusInternalServerError:
|
case http.StatusInternalServerError:
|
||||||
return fail("InternalServerError", msg)
|
fail("InternalServerError", msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
fmt.Fprintln(os.Stdout, msg)
|
||||||
@@ -418,59 +80,11 @@ func runRestart(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runFlushQueues(c *cli.Context) error {
|
func runFlushQueues(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
setup("manager", false)
|
||||||
defer cancel()
|
statusCode, msg := private.FlushQueues(c.Duration("timeout"), c.Bool("non-blocking"))
|
||||||
|
|
||||||
setup("manager", c.Bool("debug"))
|
|
||||||
statusCode, msg := private.FlushQueues(ctx, c.Duration("timeout"), c.Bool("non-blocking"))
|
|
||||||
switch statusCode {
|
switch statusCode {
|
||||||
case http.StatusInternalServerError:
|
case http.StatusInternalServerError:
|
||||||
return fail("InternalServerError", msg)
|
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)
|
fmt.Fprintln(os.Stdout, msg)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/models/migrations"
|
"code.gitea.io/gitea/models/migrations"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
@@ -24,20 +24,17 @@ var CmdMigrate = cli.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runMigrate(ctx *cli.Context) error {
|
func runMigrate(ctx *cli.Context) error {
|
||||||
stdCtx, cancel := installSignals()
|
if err := initDB(); err != nil {
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(stdCtx); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("AppPath: %s", setting.AppPath)
|
log.Trace("AppPath: %s", setting.AppPath)
|
||||||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
log.Trace("AppWorkPath: %s", setting.AppWorkPath)
|
||||||
log.Info("Custom path: %s", setting.CustomPath)
|
log.Trace("Custom path: %s", setting.CustomPath)
|
||||||
log.Info("Log path: %s", setting.LogRootPath)
|
log.Trace("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)
|
log.Fatal("Failed to initialize ORM engine: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,196 +0,0 @@
|
|||||||
// 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 (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/db"
|
|
||||||
"code.gitea.io/gitea/models/migrations"
|
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
"code.gitea.io/gitea/modules/storage"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CmdMigrateStorage represents the available migrate storage sub-command.
|
|
||||||
var CmdMigrateStorage = cli.Command{
|
|
||||||
Name: "migrate-storage",
|
|
||||||
Usage: "Migrate the storage",
|
|
||||||
Description: "This is a command for migrating storage.",
|
|
||||||
Action: runMigrateStorage,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "type, t",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Kinds of files to migrate, currently only 'attachments' is supported",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "storage, s",
|
|
||||||
Value: "",
|
|
||||||
Usage: "New storage type: local (default) or minio",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "path, p",
|
|
||||||
Value: "",
|
|
||||||
Usage: "New storage placement if store is local (leave blank for default)",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "minio-endpoint",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Minio storage endpoint",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "minio-access-key-id",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Minio storage accessKeyID",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "minio-secret-access-key",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Minio storage secretAccessKey",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "minio-bucket",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Minio storage bucket",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "minio-location",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Minio storage location to create bucket",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "minio-base-path",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Minio storage basepath on the bucket",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "minio-use-ssl",
|
|
||||||
Usage: "Enable SSL for minio",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func migrateAttachments(dstStorage storage.ObjectStorage) error {
|
|
||||||
return repo_model.IterateAttachment(func(attach *repo_model.Attachment) error {
|
|
||||||
_, err := storage.Copy(dstStorage, attach.RelativePath(), storage.Attachments, attach.RelativePath())
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
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(dstStorage storage.ObjectStorage) error {
|
|
||||||
return user_model.IterateUser(func(user *user_model.User) error {
|
|
||||||
_, err := storage.Copy(dstStorage, user.CustomAvatarRelativePath(), storage.Avatars, user.CustomAvatarRelativePath())
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func migrateRepoAvatars(dstStorage storage.ObjectStorage) error {
|
|
||||||
return repo_model.IterateRepository(func(repo *repo_model.Repository) error {
|
|
||||||
_, err := storage.Copy(dstStorage, repo.CustomAvatarRelativePath(), storage.RepoAvatars, repo.CustomAvatarRelativePath())
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func runMigrateStorage(ctx *cli.Context) error {
|
|
||||||
stdCtx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if err := initDB(stdCtx); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("AppPath: %s", setting.AppPath)
|
|
||||||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
|
||||||
log.Info("Custom path: %s", setting.CustomPath)
|
|
||||||
log.Info("Log path: %s", setting.LogRootPath)
|
|
||||||
log.Info("Configuration file: %s", setting.CustomConf)
|
|
||||||
|
|
||||||
if err := db.InitEngineWithMigration(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
|
|
||||||
}
|
|
||||||
|
|
||||||
var dstStorage storage.ObjectStorage
|
|
||||||
var err error
|
|
||||||
switch strings.ToLower(ctx.String("storage")) {
|
|
||||||
case "":
|
|
||||||
fallthrough
|
|
||||||
case string(storage.LocalStorageType):
|
|
||||||
p := ctx.String("path")
|
|
||||||
if p == "" {
|
|
||||||
log.Fatal("Path must be given when storage is loal")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
dstStorage, err = storage.NewLocalStorage(
|
|
||||||
goCtx,
|
|
||||||
storage.LocalStorageConfig{
|
|
||||||
Path: p,
|
|
||||||
})
|
|
||||||
case string(storage.MinioStorageType):
|
|
||||||
dstStorage, err = storage.NewMinioStorage(
|
|
||||||
goCtx,
|
|
||||||
storage.MinioStorageConfig{
|
|
||||||
Endpoint: ctx.String("minio-endpoint"),
|
|
||||||
AccessKeyID: ctx.String("minio-access-key-id"),
|
|
||||||
SecretAccessKey: ctx.String("minio-secret-access-key"),
|
|
||||||
Bucket: ctx.String("minio-bucket"),
|
|
||||||
Location: ctx.String("minio-location"),
|
|
||||||
BasePath: ctx.String("minio-base-path"),
|
|
||||||
UseSSL: ctx.Bool("minio-use-ssl"),
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("Unsupported storage type: %s", ctx.String("storage"))
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tp := strings.ToLower(ctx.String("type"))
|
|
||||||
switch tp {
|
|
||||||
case "attachments":
|
|
||||||
if err := migrateAttachments(dstStorage); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
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"))
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Warn("All files have been copied to the new placement but old files are still on the original placement.")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
// 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 (
|
|
||||||
"errors"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/private"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CmdRestoreRepository represents the available restore a repository sub-command.
|
|
||||||
var CmdRestoreRepository = cli.Command{
|
|
||||||
Name: "restore-repo",
|
|
||||||
Usage: "Restore the repository from disk",
|
|
||||||
Description: "This is a command for restoring the repository data.",
|
|
||||||
Action: runRestoreRepository,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "repo_dir, r",
|
|
||||||
Value: "./data",
|
|
||||||
Usage: "Repository dir path to restore from",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "owner_name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Restore destination owner name",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "repo_name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Restore destination repository name",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "units",
|
|
||||||
Value: "",
|
|
||||||
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.`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func runRestoreRepository(c *cli.Context) error {
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
setting.LoadFromExisting()
|
|
||||||
|
|
||||||
statusCode, errStr := private.RestoreRepo(
|
|
||||||
ctx,
|
|
||||||
c.String("repo_dir"),
|
|
||||||
c.String("owner_name"),
|
|
||||||
c.String("repo_name"),
|
|
||||||
c.StringSlice("units"),
|
|
||||||
)
|
|
||||||
if statusCode == http.StatusOK {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Fatal("Failed to restore repository: %v", errStr)
|
|
||||||
return errors.New(errStr)
|
|
||||||
}
|
|
||||||
163
cmd/serv.go
163
cmd/serv.go
@@ -6,6 +6,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -17,18 +18,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
"code.gitea.io/gitea/modules/lfs"
|
||||||
"code.gitea.io/gitea/models/perm"
|
|
||||||
"code.gitea.io/gitea/modules/git"
|
|
||||||
"code.gitea.io/gitea/modules/json"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/pprof"
|
"code.gitea.io/gitea/modules/pprof"
|
||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/services/lfs"
|
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v4"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"github.com/kballard/go-shellquote"
|
"github.com/unknwon/com"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -53,52 +50,46 @@ var CmdServ = cli.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setup(logPath string, debug bool) {
|
func setup(logPath string, debug bool) {
|
||||||
_ = log.DelLogger("console")
|
if !debug {
|
||||||
if debug {
|
_ = log.DelLogger("console")
|
||||||
_ = log.NewLogger(1000, "console", "console", `{"level":"trace","stacktracelevel":"NONE","stderr":true}`)
|
|
||||||
} else {
|
|
||||||
_ = log.NewLogger(1000, "console", "console", `{"level":"fatal","stacktracelevel":"NONE","stderr":true}`)
|
|
||||||
}
|
}
|
||||||
setting.LoadFromExisting()
|
setting.NewContext()
|
||||||
if debug {
|
if debug {
|
||||||
setting.RunMode = "dev"
|
setting.ProdMode = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseCmd(cmd string) (string, string) {
|
||||||
|
ss := strings.SplitN(cmd, " ", 2)
|
||||||
|
if len(ss) != 2 {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
return ss[0], strings.Replace(ss[1], "'/", "'", 1)
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
allowedCommands = map[string]perm.AccessMode{
|
allowedCommands = map[string]models.AccessMode{
|
||||||
"git-upload-pack": perm.AccessModeRead,
|
"git-upload-pack": models.AccessModeRead,
|
||||||
"git-upload-archive": perm.AccessModeRead,
|
"git-upload-archive": models.AccessModeRead,
|
||||||
"git-receive-pack": perm.AccessModeWrite,
|
"git-receive-pack": models.AccessModeWrite,
|
||||||
lfsAuthenticateVerb: perm.AccessModeNone,
|
lfsAuthenticateVerb: models.AccessModeNone,
|
||||||
}
|
}
|
||||||
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func fail(userMessage, logMessage string, args ...interface{}) error {
|
func fail(userMessage, logMessage string, args ...interface{}) {
|
||||||
// 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.Stderr, "Gitea:", userMessage)
|
||||||
|
|
||||||
if len(logMessage) > 0 {
|
if len(logMessage) > 0 {
|
||||||
if !setting.IsProd {
|
if !setting.ProdMode {
|
||||||
fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
|
fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if len(logMessage) > 0 {
|
os.Exit(1)
|
||||||
_ = private.SSHLog(ctx, true, fmt.Sprintf(logMessage+": ", args...))
|
|
||||||
}
|
|
||||||
return cli.NewExitError("", 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runServ(c *cli.Context) error {
|
func runServ(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
// FIXME: This needs to internationalised
|
// FIXME: This needs to internationalised
|
||||||
setup("serv.log", c.Bool("debug"))
|
setup("serv.log", c.Bool("debug"))
|
||||||
|
|
||||||
@@ -116,136 +107,107 @@ func runServ(c *cli.Context) error {
|
|||||||
|
|
||||||
keys := strings.Split(c.Args()[0], "-")
|
keys := strings.Split(c.Args()[0], "-")
|
||||||
if len(keys) != 2 || keys[0] != "key" {
|
if len(keys) != 2 || keys[0] != "key" {
|
||||||
return fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
|
fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
|
||||||
}
|
|
||||||
keyID, err := strconv.ParseInt(keys[1], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return fail("Key ID format error", "Invalid key argument: %s", c.Args()[1])
|
|
||||||
}
|
}
|
||||||
|
keyID := com.StrTo(keys[1]).MustInt64()
|
||||||
|
|
||||||
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
|
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
|
||||||
if len(cmd) == 0 {
|
if len(cmd) == 0 {
|
||||||
key, user, err := private.ServNoCommand(ctx, keyID)
|
key, user, err := private.ServNoCommand(keyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fail("Internal error", "Failed to check provided key: %v", err)
|
fail("Internal error", "Failed to check provided key: %v", err)
|
||||||
}
|
}
|
||||||
switch key.Type {
|
if key.Type == models.KeyTypeDeploy {
|
||||||
case asymkey_model.KeyTypeDeploy:
|
|
||||||
println("Hi there! You've successfully authenticated with the deploy key named " + key.Name + ", but Gitea does not provide shell access.")
|
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:
|
} else {
|
||||||
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.")
|
println("Hi there, " + user.Name + "! You've successfully authenticated with the key named " + key.Name + ", but Gitea does not provide shell access.")
|
||||||
}
|
}
|
||||||
println("If this is unexpected, please log in with password and setup Gitea under another user.")
|
println("If this is unexpected, please log in with password and setup Gitea under another user.")
|
||||||
return nil
|
return nil
|
||||||
} else if c.Bool("debug") {
|
|
||||||
log.Debug("SSH_ORIGINAL_COMMAND: %s", os.Getenv("SSH_ORIGINAL_COMMAND"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
words, err := shellquote.Split(cmd)
|
verb, args := parseCmd(cmd)
|
||||||
if err != nil {
|
|
||||||
return fail("Error parsing arguments", "Failed to parse arguments: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
verb := words[0]
|
|
||||||
repoPath := words[1]
|
|
||||||
if repoPath[0] == '/' {
|
|
||||||
repoPath = repoPath[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
var lfsVerb string
|
var lfsVerb string
|
||||||
if verb == lfsAuthenticateVerb {
|
if verb == lfsAuthenticateVerb {
|
||||||
if !setting.LFS.StartServer {
|
if !setting.LFS.StartServer {
|
||||||
return fail("Unknown git command", "LFS authentication request over SSH denied, LFS support is disabled")
|
fail("Unknown git command", "LFS authentication request over SSH denied, LFS support is disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(words) > 2 {
|
argsSplit := strings.Split(args, " ")
|
||||||
lfsVerb = words[2]
|
if len(argsSplit) >= 2 {
|
||||||
|
args = strings.TrimSpace(argsSplit[0])
|
||||||
|
lfsVerb = strings.TrimSpace(argsSplit[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LowerCase and trim the repoPath as that's how they are stored.
|
repoPath := strings.ToLower(strings.Trim(args, "'"))
|
||||||
repoPath = strings.ToLower(strings.TrimSpace(repoPath))
|
|
||||||
|
|
||||||
rr := strings.SplitN(repoPath, "/", 2)
|
rr := strings.SplitN(repoPath, "/", 2)
|
||||||
if len(rr) != 2 {
|
if len(rr) != 2 {
|
||||||
return fail("Invalid repository path", "Invalid repository path: %v", repoPath)
|
fail("Invalid repository path", "Invalid repository path: %v", args)
|
||||||
}
|
}
|
||||||
|
|
||||||
username := strings.ToLower(rr[0])
|
username := strings.ToLower(rr[0])
|
||||||
reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
|
reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
|
||||||
|
|
||||||
if alphaDashDotPattern.MatchString(reponame) {
|
if alphaDashDotPattern.MatchString(reponame) {
|
||||||
return fail("Invalid repo name", "Invalid repo name: %s", reponame)
|
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 {
|
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)
|
fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
stopCPUProfiler, err := pprof.DumpCPUProfileForUsername(setting.PprofDataPath, username)
|
stopCPUProfiler, err := pprof.DumpCPUProfileForUsername(setting.PprofDataPath, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fail("Internal Server Error", "Unable to start CPU profile: %v", err)
|
fail("Internal Server Error", "Unable to start CPU profile: %v", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
stopCPUProfiler()
|
stopCPUProfiler()
|
||||||
err := pprof.DumpMemProfileForUsername(setting.PprofDataPath, username)
|
err := pprof.DumpMemProfileForUsername(setting.PprofDataPath, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = fail("Internal Server Error", "Unable to dump Mem Profile: %v", err)
|
fail("Internal Server Error", "Unable to dump Mem Profile: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
requestedMode, has := allowedCommands[verb]
|
requestedMode, has := allowedCommands[verb]
|
||||||
if !has {
|
if !has {
|
||||||
return fail("Unknown git command", "Unknown git command %s", verb)
|
fail("Unknown git command", "Unknown git command %s", verb)
|
||||||
}
|
}
|
||||||
|
|
||||||
if verb == lfsAuthenticateVerb {
|
if verb == lfsAuthenticateVerb {
|
||||||
if lfsVerb == "upload" {
|
if lfsVerb == "upload" {
|
||||||
requestedMode = perm.AccessModeWrite
|
requestedMode = models.AccessModeWrite
|
||||||
} else if lfsVerb == "download" {
|
} else if lfsVerb == "download" {
|
||||||
requestedMode = perm.AccessModeRead
|
requestedMode = models.AccessModeRead
|
||||||
} else {
|
} else {
|
||||||
return fail("Unknown LFS verb", "Unknown lfs verb %s", lfsVerb)
|
fail("Unknown LFS verb", "Unknown lfs verb %s", lfsVerb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
results, err := private.ServCommand(ctx, keyID, username, reponame, requestedMode, verb, lfsVerb)
|
results, err := private.ServCommand(keyID, username, reponame, requestedMode, verb, lfsVerb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if private.IsErrServCommand(err) {
|
if private.IsErrServCommand(err) {
|
||||||
errServCommand := err.(private.ErrServCommand)
|
errServCommand := err.(private.ErrServCommand)
|
||||||
if errServCommand.StatusCode != http.StatusInternalServerError {
|
if errServCommand.StatusCode != http.StatusInternalServerError {
|
||||||
return fail("Unauthorized", "%s", errServCommand.Error())
|
fail("Unauthorized", "%s", errServCommand.Error())
|
||||||
|
} else {
|
||||||
|
fail("Internal Server Error", "%s", errServCommand.Error())
|
||||||
}
|
}
|
||||||
return fail("Internal Server Error", "%s", errServCommand.Error())
|
|
||||||
}
|
}
|
||||||
return fail("Internal Server Error", "%s", err.Error())
|
fail("Internal Server Error", "%s", err.Error())
|
||||||
}
|
}
|
||||||
os.Setenv(models.EnvRepoIsWiki, strconv.FormatBool(results.IsWiki))
|
os.Setenv(models.EnvRepoIsWiki, strconv.FormatBool(results.IsWiki))
|
||||||
os.Setenv(models.EnvRepoName, results.RepoName)
|
os.Setenv(models.EnvRepoName, results.RepoName)
|
||||||
os.Setenv(models.EnvRepoUsername, results.OwnerName)
|
os.Setenv(models.EnvRepoUsername, results.OwnerName)
|
||||||
os.Setenv(models.EnvPusherName, results.UserName)
|
os.Setenv(models.EnvPusherName, results.UserName)
|
||||||
os.Setenv(models.EnvPusherEmail, results.UserEmail)
|
|
||||||
os.Setenv(models.EnvPusherID, strconv.FormatInt(results.UserID, 10))
|
os.Setenv(models.EnvPusherID, strconv.FormatInt(results.UserID, 10))
|
||||||
os.Setenv(models.EnvRepoID, strconv.FormatInt(results.RepoID, 10))
|
os.Setenv(models.ProtectedBranchRepoID, strconv.FormatInt(results.RepoID, 10))
|
||||||
os.Setenv(models.EnvPRID, fmt.Sprintf("%d", 0))
|
os.Setenv(models.ProtectedBranchPRID, fmt.Sprintf("%d", 0))
|
||||||
os.Setenv(models.EnvIsDeployKey, fmt.Sprintf("%t", results.IsDeployKey))
|
os.Setenv(models.EnvIsDeployKey, fmt.Sprintf("%t", results.IsDeployKey))
|
||||||
os.Setenv(models.EnvKeyID, fmt.Sprintf("%d", results.KeyID))
|
os.Setenv(models.EnvKeyID, fmt.Sprintf("%d", results.KeyID))
|
||||||
os.Setenv(models.EnvAppURL, setting.AppURL)
|
|
||||||
|
|
||||||
//LFS token authentication
|
//LFS token authentication
|
||||||
if verb == lfsAuthenticateVerb {
|
if verb == lfsAuthenticateVerb {
|
||||||
@@ -253,8 +215,7 @@ func runServ(c *cli.Context) error {
|
|||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
claims := lfs.Claims{
|
claims := lfs.Claims{
|
||||||
// FIXME: we need to migrate to RegisteredClaims
|
StandardClaims: jwt.StandardClaims{
|
||||||
StandardClaims: jwt.StandardClaims{ // nolint
|
|
||||||
ExpiresAt: now.Add(setting.LFS.HTTPAuthExpiry).Unix(),
|
ExpiresAt: now.Add(setting.LFS.HTTPAuthExpiry).Unix(),
|
||||||
NotBefore: now.Unix(),
|
NotBefore: now.Unix(),
|
||||||
},
|
},
|
||||||
@@ -267,7 +228,7 @@ func runServ(c *cli.Context) error {
|
|||||||
// Sign and get the complete encoded token as a string using the secret
|
// Sign and get the complete encoded token as a string using the secret
|
||||||
tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes)
|
tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fail("Internal error", "Failed to sign JWT token: %v", err)
|
fail("Internal error", "Failed to sign JWT token: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenAuthentication := &models.LFSTokenResponse{
|
tokenAuthentication := &models.LFSTokenResponse{
|
||||||
@@ -279,7 +240,7 @@ func runServ(c *cli.Context) error {
|
|||||||
enc := json.NewEncoder(os.Stdout)
|
enc := json.NewEncoder(os.Stdout)
|
||||||
err = enc.Encode(tokenAuthentication)
|
err = enc.Encode(tokenAuthentication)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fail("Internal error", "Failed to encode LFS json response: %v", err)
|
fail("Internal error", "Failed to encode LFS json response: %v", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -292,9 +253,9 @@ func runServ(c *cli.Context) error {
|
|||||||
var gitcmd *exec.Cmd
|
var gitcmd *exec.Cmd
|
||||||
verbs := strings.Split(verb, " ")
|
verbs := strings.Split(verb, " ")
|
||||||
if len(verbs) == 2 {
|
if len(verbs) == 2 {
|
||||||
gitcmd = exec.CommandContext(ctx, verbs[0], verbs[1], repoPath)
|
gitcmd = exec.Command(verbs[0], verbs[1], repoPath)
|
||||||
} else {
|
} else {
|
||||||
gitcmd = exec.CommandContext(ctx, verb, repoPath)
|
gitcmd = exec.Command(verb, repoPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
gitcmd.Dir = setting.RepoRootPath
|
gitcmd.Dir = setting.RepoRootPath
|
||||||
@@ -302,13 +263,13 @@ func runServ(c *cli.Context) error {
|
|||||||
gitcmd.Stdin = os.Stdin
|
gitcmd.Stdin = os.Stdin
|
||||||
gitcmd.Stderr = os.Stderr
|
gitcmd.Stderr = os.Stderr
|
||||||
if err = gitcmd.Run(); err != nil {
|
if err = gitcmd.Run(); err != nil {
|
||||||
return fail("Internal error", "Failed to execute git command: %v", err)
|
fail("Internal error", "Failed to execute git command: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update user key activity.
|
// Update user key activity.
|
||||||
if results.KeyID > 0 {
|
if results.KeyID > 0 {
|
||||||
if err = private.UpdatePublicKeyInRepo(ctx, results.KeyID, results.RepoID); err != nil {
|
if err = private.UpdatePublicKeyInRepo(results.KeyID, results.RepoID); err != nil {
|
||||||
return fail("Internal error", "UpdatePublicKeyInRepo: %v", err)
|
fail("Internal error", "UpdatePublicKeyInRepo: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
255
cmd/web.go
255
cmd/web.go
@@ -7,20 +7,21 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"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/graceful"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/routers"
|
"code.gitea.io/gitea/routers"
|
||||||
"code.gitea.io/gitea/routers/install"
|
"code.gitea.io/gitea/routers/routes"
|
||||||
|
|
||||||
|
context2 "github.com/gorilla/context"
|
||||||
|
"github.com/unknwon/com"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
"golang.org/x/crypto/acme/autocert"
|
||||||
ini "gopkg.in/ini.v1"
|
ini "gopkg.in/ini.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,24 +38,11 @@ and it takes care of all the other things for you`,
|
|||||||
Value: "3000",
|
Value: "3000",
|
||||||
Usage: "Temporary port number to prevent conflict",
|
Usage: "Temporary port number to prevent conflict",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
|
||||||
Name: "install-port",
|
|
||||||
Value: "3000",
|
|
||||||
Usage: "Temporary port number to run the install page on to prevent conflict",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "pid, P",
|
Name: "pid, P",
|
||||||
Value: setting.PIDFile,
|
Value: "/var/run/gitea.pid",
|
||||||
Usage: "Custom pid file path",
|
Usage: "Custom pid file path",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "quiet, q",
|
|
||||||
Usage: "Only display Fatal logging errors until logging is set-up",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "verbose",
|
|
||||||
Usage: "Set initial logging to TRACE level until logging is properly set-up",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,27 +59,44 @@ func runHTTPRedirector() {
|
|||||||
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
||||||
})
|
})
|
||||||
|
|
||||||
var err = runHTTP("tcp", source, "HTTP Redirector", handler)
|
var err = runHTTP("tcp", source, context2.ClearHandler(handler))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to start port redirection: %v", err)
|
log.Fatal("Failed to start port redirection: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runWeb(ctx *cli.Context) error {
|
func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler) error {
|
||||||
if ctx.Bool("verbose") {
|
certManager := autocert.Manager{
|
||||||
_ = log.DelLogger("console")
|
Prompt: autocert.AcceptTOS,
|
||||||
log.NewLogger(0, "console", "console", fmt.Sprintf(`{"level": "trace", "colorize": %t, "stacktraceLevel": "none"}`, log.CanColorStdout))
|
HostPolicy: autocert.HostWhitelist(domain),
|
||||||
} else if ctx.Bool("quiet") {
|
Cache: autocert.DirCache(directory),
|
||||||
_ = log.DelLogger("console")
|
Email: email,
|
||||||
log.NewLogger(0, "console", "console", fmt.Sprintf(`{"level": "fatal", "colorize": %t, "stacktraceLevel": "none"}`, log.CanColorStdout))
|
|
||||||
}
|
}
|
||||||
defer func() {
|
go func() {
|
||||||
if panicked := recover(); panicked != nil {
|
log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
|
||||||
log.Fatal("PANIC: %v\n%s", panicked, string(log.Stack(2)))
|
// all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
|
||||||
|
var err = runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, certManager.HTTPHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
return runHTTPSWithTLSConfig("tcp", listenAddr, certManager.TLSConfig(), context2.ClearHandler(m))
|
||||||
|
}
|
||||||
|
|
||||||
|
func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != "GET" && r.Method != "HEAD" {
|
||||||
|
http.Error(w, "Use HTTPS", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Remove the trailing slash at the end of setting.AppURL, the request
|
||||||
|
// URI always contains a leading slash, which would result in a double
|
||||||
|
// slash
|
||||||
|
target := strings.TrimRight(setting.AppURL, "/") + r.URL.RequestURI()
|
||||||
|
http.Redirect(w, r, target, http.StatusFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runWeb(ctx *cli.Context) error {
|
||||||
managerCtx, cancel := context.WithCancel(context.Background())
|
managerCtx, cancel := context.WithCancel(context.Background())
|
||||||
graceful.InitManager(managerCtx)
|
graceful.InitManager(managerCtx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -104,40 +109,59 @@ func runWeb(ctx *cli.Context) error {
|
|||||||
|
|
||||||
// Set pid file setting
|
// Set pid file setting
|
||||||
if ctx.IsSet("pid") {
|
if ctx.IsSet("pid") {
|
||||||
setting.PIDFile = ctx.String("pid")
|
setting.CustomPID = ctx.String("pid")
|
||||||
setting.WritePIDFile = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform pre-initialization
|
// Perform global initialization
|
||||||
needsInstall := install.PreloadSettings(graceful.GetManager().HammerContext())
|
routers.GlobalInit(graceful.GetManager().HammerContext())
|
||||||
if needsInstall {
|
|
||||||
// Flag for port number in case first time run conflict
|
// Set up Macaron
|
||||||
if ctx.IsSet("port") {
|
m := routes.NewMacaron()
|
||||||
if err := setPort(ctx.String("port")); err != nil {
|
routes.RegisterRoutes(m)
|
||||||
return err
|
|
||||||
}
|
// Flag for port number in case first time run conflict.
|
||||||
}
|
if ctx.IsSet("port") {
|
||||||
if ctx.IsSet("install-port") {
|
setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, ctx.String("port"), 1)
|
||||||
if err := setPort(ctx.String("install-port")); err != nil {
|
setting.HTTPPort = ctx.String("port")
|
||||||
return err
|
|
||||||
}
|
switch setting.Protocol {
|
||||||
}
|
case setting.UnixSocket:
|
||||||
c := install.Routes()
|
case setting.FCGI:
|
||||||
err := listen(c, false)
|
case setting.FCGIUnix:
|
||||||
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()
|
|
||||||
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
|
||||||
log.Close()
|
|
||||||
return err
|
|
||||||
default:
|
default:
|
||||||
|
// Save LOCAL_ROOT_URL if port changed
|
||||||
|
cfg := ini.Empty()
|
||||||
|
if com.IsFile(setting.CustomConf) {
|
||||||
|
// Keeps custom settings if there is already something.
|
||||||
|
if err := cfg.Append(setting.CustomConf); err != nil {
|
||||||
|
return fmt.Errorf("Failed to load custom conf '%s': %v", setting.CustomConf, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultLocalURL := string(setting.Protocol) + "://"
|
||||||
|
if setting.HTTPAddr == "0.0.0.0" {
|
||||||
|
defaultLocalURL += "localhost"
|
||||||
|
} else {
|
||||||
|
defaultLocalURL += setting.HTTPAddr
|
||||||
|
}
|
||||||
|
defaultLocalURL += ":" + setting.HTTPPort + "/"
|
||||||
|
|
||||||
|
cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
|
||||||
|
|
||||||
|
if err := cfg.SaveTo(setting.CustomConf); err != nil {
|
||||||
|
return fmt.Errorf("Error saving generated JWT Secret to custom config: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
NoInstallListener()
|
|
||||||
|
listenAddr := setting.HTTPAddr
|
||||||
|
if setting.Protocol != setting.UnixSocket && setting.Protocol != setting.FCGIUnix {
|
||||||
|
listenAddr += ":" + setting.HTTPPort
|
||||||
|
}
|
||||||
|
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
|
||||||
|
|
||||||
|
if setting.LFS.StartServer {
|
||||||
|
log.Info("LFS server enabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
if setting.EnablePprof {
|
if setting.EnablePprof {
|
||||||
@@ -147,109 +171,31 @@ func runWeb(ctx *cli.Context) error {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override the provided port number within the configuration
|
|
||||||
if ctx.IsSet("port") {
|
|
||||||
if err := setPort(ctx.String("port")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up Chi routes
|
|
||||||
c := routers.NormalRoutes()
|
|
||||||
err := listen(c, true)
|
|
||||||
<-graceful.GetManager().Done()
|
|
||||||
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
|
||||||
log.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func setPort(port string) error {
|
|
||||||
setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, port, 1)
|
|
||||||
setting.HTTPPort = port
|
|
||||||
|
|
||||||
switch setting.Protocol {
|
|
||||||
case setting.HTTPUnix:
|
|
||||||
case setting.FCGI:
|
|
||||||
case setting.FCGIUnix:
|
|
||||||
default:
|
|
||||||
defaultLocalURL := string(setting.Protocol) + "://"
|
|
||||||
if setting.HTTPAddr == "0.0.0.0" {
|
|
||||||
defaultLocalURL += "localhost"
|
|
||||||
} else {
|
|
||||||
defaultLocalURL += setting.HTTPAddr
|
|
||||||
}
|
|
||||||
defaultLocalURL += ":" + setting.HTTPPort + "/"
|
|
||||||
|
|
||||||
// Save LOCAL_ROOT_URL if port changed
|
|
||||||
setting.CreateOrAppendToCustomConf(func(cfg *ini.File) {
|
|
||||||
cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func listen(m http.Handler, handleRedirector bool) error {
|
|
||||||
listenAddr := setting.HTTPAddr
|
|
||||||
if setting.Protocol != setting.HTTPUnix && setting.Protocol != setting.FCGIUnix {
|
|
||||||
listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort)
|
|
||||||
}
|
|
||||||
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.
|
|
||||||
// And this is also very helpful to maintainers to provide help to users to resolve their configuration problems.
|
|
||||||
log.Info("AppURL(ROOT_URL): %s", setting.AppURL)
|
|
||||||
|
|
||||||
if setting.LFS.StartServer {
|
|
||||||
log.Info("LFS server enabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
switch setting.Protocol {
|
switch setting.Protocol {
|
||||||
case setting.HTTP:
|
case setting.HTTP:
|
||||||
if handleRedirector {
|
NoHTTPRedirector()
|
||||||
NoHTTPRedirector()
|
err = runHTTP("tcp", listenAddr, context2.ClearHandler(m))
|
||||||
}
|
|
||||||
err = runHTTP("tcp", listenAddr, "Web", m)
|
|
||||||
case setting.HTTPS:
|
case setting.HTTPS:
|
||||||
if setting.EnableLetsEncrypt {
|
if setting.EnableLetsEncrypt {
|
||||||
err = runLetsEncrypt(listenAddr, setting.Domain, setting.LetsEncryptDirectory, setting.LetsEncryptEmail, m)
|
err = runLetsEncrypt(listenAddr, setting.Domain, setting.LetsEncryptDirectory, setting.LetsEncryptEmail, context2.ClearHandler(m))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if handleRedirector {
|
if setting.RedirectOtherPort {
|
||||||
if setting.RedirectOtherPort {
|
go runHTTPRedirector()
|
||||||
go runHTTPRedirector()
|
} else {
|
||||||
} else {
|
NoHTTPRedirector()
|
||||||
NoHTTPRedirector()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, m)
|
err = runHTTPS("tcp", listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
|
||||||
case setting.FCGI:
|
case setting.FCGI:
|
||||||
if handleRedirector {
|
NoHTTPRedirector()
|
||||||
NoHTTPRedirector()
|
err = runFCGI("tcp", listenAddr, context2.ClearHandler(m))
|
||||||
}
|
case setting.UnixSocket:
|
||||||
err = runFCGI("tcp", listenAddr, "FCGI Web", m)
|
NoHTTPRedirector()
|
||||||
case setting.HTTPUnix:
|
err = runHTTP("unix", listenAddr, context2.ClearHandler(m))
|
||||||
if handleRedirector {
|
|
||||||
NoHTTPRedirector()
|
|
||||||
}
|
|
||||||
err = runHTTP("unix", listenAddr, "Web", m)
|
|
||||||
case setting.FCGIUnix:
|
case setting.FCGIUnix:
|
||||||
if handleRedirector {
|
NoHTTPRedirector()
|
||||||
NoHTTPRedirector()
|
err = runFCGI("unix", listenAddr, context2.ClearHandler(m))
|
||||||
}
|
|
||||||
err = runFCGI("unix", listenAddr, "Web", m)
|
|
||||||
default:
|
default:
|
||||||
log.Fatal("Invalid protocol: %s", setting.Protocol)
|
log.Fatal("Invalid protocol: %s", setting.Protocol)
|
||||||
}
|
}
|
||||||
@@ -258,5 +204,8 @@ func listen(m http.Handler, handleRedirector bool) error {
|
|||||||
log.Critical("Failed to start server: %v", err)
|
log.Critical("Failed to start server: %v", err)
|
||||||
}
|
}
|
||||||
log.Info("HTTP Listener: %s Closed", listenAddr)
|
log.Info("HTTP Listener: %s Closed", listenAddr)
|
||||||
return err
|
<-graceful.GetManager().Done()
|
||||||
|
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
||||||
|
log.Close()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,18 +5,25 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/fcgi"
|
"net/http/fcgi"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/graceful"
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func runHTTP(network, listenAddr, name string, m http.Handler) error {
|
func runHTTP(network, listenAddr string, m http.Handler) error {
|
||||||
return graceful.HTTPListenAndServe(network, listenAddr, name, m)
|
return graceful.HTTPListenAndServe(network, listenAddr, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runHTTPS(network, listenAddr, certFile, keyFile string, m http.Handler) error {
|
||||||
|
return graceful.HTTPListenAndServeTLS(network, listenAddr, certFile, keyFile, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runHTTPSWithTLSConfig(network, listenAddr string, tlsConfig *tls.Config, m http.Handler) error {
|
||||||
|
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, tlsConfig, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
|
// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
|
||||||
@@ -30,23 +37,12 @@ func NoMainListener() {
|
|||||||
graceful.GetManager().InformCleanup()
|
graceful.GetManager().InformCleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NoInstallListener tells our cleanup routine that we will not be using a possibly provided listener
|
func runFCGI(network, listenAddr string, m http.Handler) error {
|
||||||
// for our install HTTP/HTTPS service
|
|
||||||
func NoInstallListener() {
|
|
||||||
graceful.GetManager().InformCleanup()
|
|
||||||
}
|
|
||||||
|
|
||||||
func runFCGI(network, listenAddr, name string, m http.Handler) error {
|
|
||||||
// This needs to handle stdin as fcgi point
|
// This needs to handle stdin as fcgi point
|
||||||
fcgiServer := graceful.NewServer(network, listenAddr, name)
|
fcgiServer := graceful.NewServer(network, listenAddr)
|
||||||
|
|
||||||
err := fcgiServer.ListenAndServe(func(listener net.Listener) error {
|
err := fcgiServer.ListenAndServe(func(listener net.Listener) error {
|
||||||
return fcgi.Serve(listener, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
return fcgi.Serve(listener, m)
|
||||||
if setting.AppSubURL != "" {
|
|
||||||
req.URL.Path = strings.TrimPrefix(req.URL.Path, setting.AppSubURL)
|
|
||||||
}
|
|
||||||
m.ServeHTTP(resp, req)
|
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to start FCGI main server: %v", err)
|
log.Fatal("Failed to start FCGI main server: %v", err)
|
||||||
|
|||||||
192
cmd/web_https.go
192
cmd/web_https.go
@@ -1,192 +0,0 @@
|
|||||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
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) 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
func runHTTPSWithTLSConfig(network, listenAddr, name string, tlsConfig *tls.Config, m http.Handler) error {
|
|
||||||
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m)
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
// 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/graceful"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
|
|
||||||
"github.com/caddyserver/certmagic"
|
|
||||||
)
|
|
||||||
|
|
||||||
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(graceful.GetManager().HammerContext(), []string{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() {
|
|
||||||
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, 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)
|
|
||||||
}
|
|
||||||
@@ -22,13 +22,11 @@ The environment variables should be of the form:
|
|||||||
|
|
||||||
GITEA__SECTION_NAME__KEY_NAME
|
GITEA__SECTION_NAME__KEY_NAME
|
||||||
|
|
||||||
Note, SECTION_NAME in the notation above is case-insensitive.
|
|
||||||
|
|
||||||
Environment variables are usually restricted to a reduced character
|
Environment variables are usually restricted to a reduced character
|
||||||
set "0-9A-Z_" - in order to allow the setting of sections with
|
set "0-9A-Z_" - in order to allow the setting of sections with
|
||||||
characters outside of that set, they should be escaped as following:
|
characters outside of that set, they should be escaped as following:
|
||||||
"_0X2E_" for "." and "_0X2D_" for "-". The entire section and key names
|
"_0X2E_" for ".". The entire section and key names can be escaped as
|
||||||
can be escaped as a UTF8 byte string if necessary. E.g. to configure:
|
a UTF8 byte string if necessary. E.g. to configure:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
@@ -42,6 +40,27 @@ You would set the environment variables: "GITEA__LOG_0x2E_CONSOLE__COLORIZE=fals
|
|||||||
and "GITEA__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found
|
and "GITEA__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found
|
||||||
on the configuration cheat sheet.
|
on the configuration cheat sheet.
|
||||||
|
|
||||||
To build locally, run:
|
To plug this command in to the docker, you simply compile the provided go file using:
|
||||||
|
|
||||||
|
go build environment-to-ini.go
|
||||||
|
|
||||||
|
And copy the resulting `environment-to-ini` command to /app/gitea in the docker.
|
||||||
|
|
||||||
|
Apply the below patch to /etc/s6/gitea.setup to wire this in.
|
||||||
|
|
||||||
|
If you find this useful please comment on #7287
|
||||||
|
|
||||||
|
|
||||||
|
diff --git a/docker/root/etc/s6/gitea/setup b/docker/root/etc/s6/gitea/setup
|
||||||
|
index f87ce9115..565bfcba9 100755
|
||||||
|
--- a/docker/root/etc/s6/gitea/setup
|
||||||
|
+++ b/docker/root/etc/s6/gitea/setup
|
||||||
|
@@ -44,6 +44,8 @@ if [ ! -f ${GITEA_CUSTOM}/conf/app.ini ]; then
|
||||||
|
SECRET_KEY=${SECRET_KEY:-""} \
|
||||||
|
envsubst < /etc/templates/app.ini > ${GITEA_CUSTOM}/conf/app.ini
|
||||||
|
|
||||||
|
+ /app/gitea/environment-to-ini -c ${GITEA_CUSTOM}/conf/app.ini
|
||||||
|
+
|
||||||
|
chown ${USER}:git ${GITEA_CUSTOM}/conf/app.ini
|
||||||
|
fi
|
||||||
|
|
||||||
go build contrib/environment-to-ini/environment-to-ini.go
|
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
|
|
||||||
|
"github.com/unknwon/com"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
ini "gopkg.in/ini.v1"
|
ini "gopkg.in/ini.v1"
|
||||||
)
|
)
|
||||||
@@ -97,11 +97,7 @@ func runEnvironmentToIni(c *cli.Context) error {
|
|||||||
setting.SetCustomPathAndConf(providedCustom, providedConf, providedWorkPath)
|
setting.SetCustomPathAndConf(providedCustom, providedConf, providedWorkPath)
|
||||||
|
|
||||||
cfg := ini.Empty()
|
cfg := ini.Empty()
|
||||||
isFile, err := util.IsFile(setting.CustomConf)
|
if com.IsFile(setting.CustomConf) {
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Unable to check if %s is a file. Error: %v", setting.CustomConf, err)
|
|
||||||
}
|
|
||||||
if isFile {
|
|
||||||
if err := cfg.Append(setting.CustomConf); err != nil {
|
if err := cfg.Append(setting.CustomConf); err != nil {
|
||||||
log.Fatal("Failed to load custom conf '%s': %v", setting.CustomConf, err)
|
log.Fatal("Failed to load custom conf '%s': %v", setting.CustomConf, err)
|
||||||
}
|
}
|
||||||
@@ -110,8 +106,6 @@ func runEnvironmentToIni(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
cfg.NameMapper = ini.SnackCase
|
cfg.NameMapper = ini.SnackCase
|
||||||
|
|
||||||
changed := false
|
|
||||||
|
|
||||||
prefix := c.String("prefix") + "__"
|
prefix := c.String("prefix") + "__"
|
||||||
|
|
||||||
for _, kv := range os.Environ() {
|
for _, kv := range os.Environ() {
|
||||||
@@ -145,22 +139,15 @@ func runEnvironmentToIni(c *cli.Context) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
oldValue := key.Value()
|
|
||||||
if !changed && oldValue != value {
|
|
||||||
changed = true
|
|
||||||
}
|
|
||||||
key.SetValue(value)
|
key.SetValue(value)
|
||||||
}
|
}
|
||||||
destination := c.String("out")
|
destination := c.String("out")
|
||||||
if len(destination) == 0 {
|
if len(destination) == 0 {
|
||||||
destination = setting.CustomConf
|
destination = setting.CustomConf
|
||||||
}
|
}
|
||||||
if destination != setting.CustomConf || changed {
|
err := cfg.SaveTo(destination)
|
||||||
log.Info("Settings saved to: %q", destination)
|
if err != nil {
|
||||||
err = cfg.SaveTo(destination)
|
return err
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if c.Bool("clear") {
|
if c.Bool("clear") {
|
||||||
for _, kv := range os.Environ() {
|
for _, kv := range os.Environ() {
|
||||||
@@ -225,6 +212,7 @@ func DecodeSectionKey(encoded string) (string, string) {
|
|||||||
if !inKey {
|
if !inKey {
|
||||||
if splitter := strings.Index(remaining, "__"); splitter > -1 {
|
if splitter := strings.Index(remaining, "__"); splitter > -1 {
|
||||||
section += remaining[:splitter]
|
section += remaining[:splitter]
|
||||||
|
inKey = true
|
||||||
key += remaining[splitter+2:]
|
key += remaining[splitter+2:]
|
||||||
} else {
|
} else {
|
||||||
section += remaining
|
section += remaining
|
||||||
@@ -232,6 +220,5 @@ func DecodeSectionKey(encoded string) (string, string) {
|
|||||||
} else {
|
} else {
|
||||||
key += remaining
|
key += remaining
|
||||||
}
|
}
|
||||||
section = strings.ToLower(section)
|
|
||||||
return section, key
|
return section, key
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/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
|
# It assumes that you place this script as gitea in /usr/bin
|
||||||
#
|
#
|
||||||
@@ -36,7 +36,7 @@ if [ -z "$APP_INI_SET" ]; then
|
|||||||
CONF_ARG="-c \"$APP_INI\""
|
CONF_ARG="-c \"$APP_INI\""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Provide FHS compliant defaults
|
# Provide FHS compliant defaults to
|
||||||
GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" exec -a "$0" "$GITEA" $CONF_ARG "$@"
|
GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" "$GITEA" $CONF_ARG "$@"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// To generate derivative fixtures, execute the following from Gitea's repository base dir:
|
// To generate derivative fixtures, execute the following from Gitea's repository base dir:
|
||||||
@@ -31,13 +31,11 @@ var (
|
|||||||
func main() {
|
func main() {
|
||||||
pathToGiteaRoot := "."
|
pathToGiteaRoot := "."
|
||||||
fixturesDir = filepath.Join(pathToGiteaRoot, "models", "fixtures")
|
fixturesDir = filepath.Join(pathToGiteaRoot, "models", "fixtures")
|
||||||
if err := unittest.CreateTestEngine(unittest.FixturesOptions{
|
if err := models.CreateTestEngine(fixturesDir); err != nil {
|
||||||
Dir: fixturesDir,
|
|
||||||
}); err != nil {
|
|
||||||
fmt.Printf("CreateTestEngine: %+v", err)
|
fmt.Printf("CreateTestEngine: %+v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if err := unittest.PrepareTestDatabase(); err != nil {
|
if err := models.PrepareTestDatabase(); err != nil {
|
||||||
fmt.Printf("PrepareTestDatabase: %+v\n", err)
|
fmt.Printf("PrepareTestDatabase: %+v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
@@ -66,7 +64,7 @@ func generate(name string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
path := filepath.Join(fixturesDir, name+".yml")
|
path := filepath.Join(fixturesDir, name+".yml")
|
||||||
if err := os.WriteFile(path, []byte(data), 0644); err != nil {
|
if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil {
|
||||||
return fmt.Errorf("%s: %+v", path, err)
|
return fmt.Errorf("%s: %+v", path, err)
|
||||||
}
|
}
|
||||||
fmt.Printf("%s created.\n", path)
|
fmt.Printf("%s created.\n", path)
|
||||||
|
|||||||
2
contrib/gitea-monitoring-mixin/.gitignore
vendored
2
contrib/gitea-monitoring-mixin/.gitignore
vendored
@@ -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.
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
{
|
|
||||||
_config+:: {
|
|
||||||
local c = self,
|
|
||||||
dashboardNamePrefix: 'Gitea',
|
|
||||||
dashboardTags: ['gitea'],
|
|
||||||
dashboardPeriod: 'now-1h',
|
|
||||||
dashboardTimezone: 'default',
|
|
||||||
dashboardRefresh: '1m',
|
|
||||||
|
|
||||||
// please see https://docs.gitea.io/en-us/config-cheat-sheet/#metrics-metrics
|
|
||||||
// Show issue by repository metrics with format gitea_issues_by_repository{repository="org/repo"} 5.
|
|
||||||
// Requires Gitea 1.16.0 with ENABLED_ISSUE_BY_REPOSITORY set to true.
|
|
||||||
showIssuesByRepository: true,
|
|
||||||
// Show graphs for issue by label metrics with format gitea_issues_by_label{label="bug"} 2.
|
|
||||||
// Requires Gitea 1.16.0 with ENABLED_ISSUE_BY_LABEL set to true.
|
|
||||||
showIssuesByLabel: true,
|
|
||||||
|
|
||||||
// Requires Gitea 1.16.0.
|
|
||||||
showIssuesOpenClose: true,
|
|
||||||
|
|
||||||
// add or remove metrics from dashboard
|
|
||||||
giteaStatMetrics:
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name: 'gitea_organizations',
|
|
||||||
description: 'Organizations',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'gitea_teams',
|
|
||||||
description: 'Teams',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'gitea_users',
|
|
||||||
description: 'Users',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'gitea_repositories',
|
|
||||||
description: 'Repositories',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'gitea_milestones',
|
|
||||||
description: 'Milestones',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'gitea_stars',
|
|
||||||
description: 'Stars',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'gitea_releases',
|
|
||||||
description: 'Releases',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
+
|
|
||||||
if c.showIssuesOpenClose then
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name: 'gitea_issues_open',
|
|
||||||
description: 'Issues opened',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'gitea_issues_closed',
|
|
||||||
description: 'Issues closed',
|
|
||||||
},
|
|
||||||
] else
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name: 'gitea_issues',
|
|
||||||
description: 'Issues',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
//set this for using label colors on graphs
|
|
||||||
issueLabels: [
|
|
||||||
{
|
|
||||||
label: 'bug',
|
|
||||||
color: '#ee0701',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'duplicate',
|
|
||||||
color: '#cccccc',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'invalid',
|
|
||||||
color: '#e6e6e6',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'enhancement',
|
|
||||||
color: '#84b6eb',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'help wanted',
|
|
||||||
color: '#128a0c',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'question',
|
|
||||||
color: '#cc317c',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
(import 'overview.libsonnet')
|
|
||||||
@@ -1,461 +0,0 @@
|
|||||||
local grafana = import 'github.com/grafana/grafonnet-lib/grafonnet/grafana.libsonnet';
|
|
||||||
local prometheus = grafana.prometheus;
|
|
||||||
|
|
||||||
local addIssueLabelsOverrides(labels) =
|
|
||||||
{
|
|
||||||
fieldConfig+: {
|
|
||||||
overrides+: [
|
|
||||||
{
|
|
||||||
matcher: {
|
|
||||||
id: 'byRegexp',
|
|
||||||
options: label.label,
|
|
||||||
},
|
|
||||||
properties: [
|
|
||||||
{
|
|
||||||
id: 'color',
|
|
||||||
value: {
|
|
||||||
fixedColor: label.color,
|
|
||||||
mode: 'fixed',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
for label in labels
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
grafanaDashboards+:: {
|
|
||||||
|
|
||||||
local giteaSelector = 'job="$job", instance="$instance"',
|
|
||||||
local giteaStatsPanel =
|
|
||||||
grafana.statPanel.new(
|
|
||||||
'Gitea stats',
|
|
||||||
datasource='$datasource',
|
|
||||||
reducerFunction='lastNotNull',
|
|
||||||
graphMode='none',
|
|
||||||
colorMode='value',
|
|
||||||
)
|
|
||||||
.addTargets(
|
|
||||||
[
|
|
||||||
prometheus.target(expr='%s{%s}' % [metric.name, giteaSelector], legendFormat=metric.description, intervalFactor=10)
|
|
||||||
for metric in $._config.giteaStatMetrics
|
|
||||||
]
|
|
||||||
)
|
|
||||||
+ {
|
|
||||||
fieldConfig+: {
|
|
||||||
defaults+: {
|
|
||||||
color: {
|
|
||||||
fixedColor: 'blue',
|
|
||||||
mode: 'fixed',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
local giteaUptimePanel =
|
|
||||||
grafana.statPanel.new(
|
|
||||||
'Uptime',
|
|
||||||
datasource='$datasource',
|
|
||||||
reducerFunction='last',
|
|
||||||
graphMode='area',
|
|
||||||
colorMode='value',
|
|
||||||
)
|
|
||||||
.addTarget(prometheus.target(expr='time()-process_start_time_seconds{%s}' % giteaSelector, intervalFactor=1))
|
|
||||||
+ {
|
|
||||||
fieldConfig+: {
|
|
||||||
defaults+: {
|
|
||||||
color: {
|
|
||||||
fixedColor: 'blue',
|
|
||||||
mode: 'fixed',
|
|
||||||
},
|
|
||||||
unit: 's',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
local giteaMemoryPanel =
|
|
||||||
grafana.graphPanel.new(
|
|
||||||
'Memory usage',
|
|
||||||
datasource='$datasource'
|
|
||||||
)
|
|
||||||
.addTarget(prometheus.target(expr='process_resident_memory_bytes{%s}' % giteaSelector, intervalFactor=2))
|
|
||||||
+ {
|
|
||||||
type: 'timeseries',
|
|
||||||
options+: {
|
|
||||||
tooltip: {
|
|
||||||
mode: 'multi',
|
|
||||||
},
|
|
||||||
legend+: {
|
|
||||||
displayMode: 'hidden',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fieldConfig+: {
|
|
||||||
defaults+: {
|
|
||||||
custom+: {
|
|
||||||
lineInterpolation: 'smooth',
|
|
||||||
fillOpacity: 15,
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
fixedColor: 'green',
|
|
||||||
mode: 'fixed',
|
|
||||||
},
|
|
||||||
unit: 'decbytes',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
local giteaCpuPanel =
|
|
||||||
grafana.graphPanel.new(
|
|
||||||
'CPU usage',
|
|
||||||
datasource='$datasource'
|
|
||||||
)
|
|
||||||
.addTarget(prometheus.target(expr='rate(process_cpu_seconds_total{%s}[$__rate_interval])*100' % giteaSelector, intervalFactor=2))
|
|
||||||
+ {
|
|
||||||
type: 'timeseries',
|
|
||||||
options+: {
|
|
||||||
tooltip: {
|
|
||||||
mode: 'multi',
|
|
||||||
},
|
|
||||||
legend+: {
|
|
||||||
displayMode: 'hidden',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fieldConfig+: {
|
|
||||||
defaults+: {
|
|
||||||
custom+: {
|
|
||||||
lineInterpolation: 'smooth',
|
|
||||||
gradientMode: 'scheme',
|
|
||||||
fillOpacity: 15,
|
|
||||||
axisSoftMin: 0,
|
|
||||||
axisSoftMax: 0,
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
mode: 'continuous-GrYlRd', // from green to red (100%)
|
|
||||||
},
|
|
||||||
unit: 'percent',
|
|
||||||
},
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
matcher: {
|
|
||||||
id: 'byRegexp',
|
|
||||||
options: '.+',
|
|
||||||
},
|
|
||||||
properties: [
|
|
||||||
{
|
|
||||||
id: 'max',
|
|
||||||
value: 100,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'min',
|
|
||||||
value: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
local giteaFileDescriptorsPanel =
|
|
||||||
grafana.graphPanel.new(
|
|
||||||
'File descriptors usage',
|
|
||||||
datasource='$datasource',
|
|
||||||
)
|
|
||||||
.addTarget(prometheus.target(expr='process_open_fds{%s}' % giteaSelector, intervalFactor=2))
|
|
||||||
.addTarget(prometheus.target(expr='process_max_fds{%s}' % giteaSelector, intervalFactor=2))
|
|
||||||
.addSeriesOverride(
|
|
||||||
{
|
|
||||||
alias: '/process_max_fds.+/',
|
|
||||||
color: '#F2495C', // red
|
|
||||||
dashes: true,
|
|
||||||
fill: 0,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
+ {
|
|
||||||
type: 'timeseries',
|
|
||||||
options+: {
|
|
||||||
tooltip: {
|
|
||||||
mode: 'multi',
|
|
||||||
},
|
|
||||||
legend+: {
|
|
||||||
displayMode: 'hidden',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fieldConfig+: {
|
|
||||||
defaults+: {
|
|
||||||
custom+: {
|
|
||||||
lineInterpolation: 'smooth',
|
|
||||||
gradientMode: 'scheme',
|
|
||||||
fillOpacity: 0,
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
fixedColor: 'green',
|
|
||||||
mode: 'fixed',
|
|
||||||
},
|
|
||||||
unit: '',
|
|
||||||
},
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
matcher: {
|
|
||||||
id: 'byFrameRefID',
|
|
||||||
options: 'B',
|
|
||||||
},
|
|
||||||
properties: [
|
|
||||||
{
|
|
||||||
id: 'custom.lineStyle',
|
|
||||||
value: {
|
|
||||||
fill: 'dash',
|
|
||||||
dash: [
|
|
||||||
10,
|
|
||||||
10,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'color',
|
|
||||||
value: {
|
|
||||||
mode: 'fixed',
|
|
||||||
fixedColor: 'red',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
local giteaChangesPanelPrototype =
|
|
||||||
grafana.graphPanel.new(
|
|
||||||
'',
|
|
||||||
datasource='$datasource',
|
|
||||||
interval='$agg_interval',
|
|
||||||
maxDataPoints=10000,
|
|
||||||
)
|
|
||||||
+ {
|
|
||||||
type: 'timeseries',
|
|
||||||
options+: {
|
|
||||||
tooltip: {
|
|
||||||
mode: 'multi',
|
|
||||||
},
|
|
||||||
legend+: {
|
|
||||||
calcs+: [
|
|
||||||
'sum',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fieldConfig+: {
|
|
||||||
defaults+: {
|
|
||||||
noValue: '0',
|
|
||||||
custom+: {
|
|
||||||
drawStyle: 'bars',
|
|
||||||
barAlignment: -1,
|
|
||||||
fillOpacity: 50,
|
|
||||||
gradientMode: 'hue',
|
|
||||||
pointSize: 1,
|
|
||||||
lineWidth: 0,
|
|
||||||
stacking: {
|
|
||||||
group: 'A',
|
|
||||||
mode: 'normal',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
local giteaChangesPanelAll =
|
|
||||||
giteaChangesPanelPrototype
|
|
||||||
.addTarget(prometheus.target(expr='changes(process_start_time_seconds{%s}[$__interval]) > 0' % [giteaSelector], legendFormat='Restarts', intervalFactor=1))
|
|
||||||
.addTargets(
|
|
||||||
[
|
|
||||||
prometheus.target(expr='floor(delta(%s{%s}[$__interval])) > 0' % [metric.name, giteaSelector], legendFormat=metric.description, intervalFactor=1)
|
|
||||||
for metric in $._config.giteaStatMetrics
|
|
||||||
]
|
|
||||||
) + { id: 200 }, // some unique number, beyond the maximum number of panels in the dashboard,
|
|
||||||
|
|
||||||
local giteaChangesPanelTotal =
|
|
||||||
grafana.statPanel.new(
|
|
||||||
'Changes',
|
|
||||||
datasource='-- Dashboard --',
|
|
||||||
reducerFunction='sum',
|
|
||||||
graphMode='none',
|
|
||||||
textMode='value_and_name',
|
|
||||||
colorMode='value',
|
|
||||||
)
|
|
||||||
+ {
|
|
||||||
targets+: [
|
|
||||||
{
|
|
||||||
panelId: giteaChangesPanelAll.id,
|
|
||||||
refId: 'A',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
+ {
|
|
||||||
fieldConfig+: {
|
|
||||||
defaults+: {
|
|
||||||
color: {
|
|
||||||
mode: 'palette-classic',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
local giteaChangesByRepositories =
|
|
||||||
giteaChangesPanelPrototype
|
|
||||||
.addTarget(prometheus.target(expr='floor(increase(gitea_issues_by_repository{%s}[$__interval])) > 0' % [giteaSelector], legendFormat='{{ repository }}', intervalFactor=1))
|
|
||||||
+ { id: 210 }, // some unique number, beyond the maximum number of panels in the dashboard,
|
|
||||||
|
|
||||||
local giteaChangesByRepositoriesTotal =
|
|
||||||
grafana.statPanel.new(
|
|
||||||
'Issues by repository',
|
|
||||||
datasource='-- Dashboard --',
|
|
||||||
reducerFunction='sum',
|
|
||||||
graphMode='none',
|
|
||||||
textMode='value_and_name',
|
|
||||||
colorMode='value',
|
|
||||||
)
|
|
||||||
+ {
|
|
||||||
id: 211,
|
|
||||||
targets+: [
|
|
||||||
{
|
|
||||||
panelId: giteaChangesByRepositories.id,
|
|
||||||
refId: 'A',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
+ {
|
|
||||||
fieldConfig+: {
|
|
||||||
defaults+: {
|
|
||||||
color: {
|
|
||||||
mode: 'palette-classic',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
local giteaChangesByLabel =
|
|
||||||
giteaChangesPanelPrototype
|
|
||||||
.addTarget(prometheus.target(expr='floor(increase(gitea_issues_by_label{%s}[$__interval])) > 0' % [giteaSelector], legendFormat='{{ label }}', intervalFactor=1))
|
|
||||||
+ addIssueLabelsOverrides($._config.issueLabels)
|
|
||||||
+ { id: 220 }, // some unique number, beyond the maximum number of panels in the dashboard,
|
|
||||||
|
|
||||||
local giteaChangesByLabelTotal =
|
|
||||||
grafana.statPanel.new(
|
|
||||||
'Issues by labels',
|
|
||||||
datasource='-- Dashboard --',
|
|
||||||
reducerFunction='sum',
|
|
||||||
graphMode='none',
|
|
||||||
textMode='value_and_name',
|
|
||||||
colorMode='value',
|
|
||||||
)
|
|
||||||
+ addIssueLabelsOverrides($._config.issueLabels)
|
|
||||||
+ {
|
|
||||||
id: 221,
|
|
||||||
targets+: [
|
|
||||||
{
|
|
||||||
panelId: giteaChangesByLabel.id,
|
|
||||||
refId: 'A',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
+ {
|
|
||||||
fieldConfig+: {
|
|
||||||
defaults+: {
|
|
||||||
color: {
|
|
||||||
mode: 'palette-classic',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
'gitea-overview.json':
|
|
||||||
grafana.dashboard.new(
|
|
||||||
'%s Overview' % $._config.dashboardNamePrefix,
|
|
||||||
time_from='%s' % $._config.dashboardPeriod,
|
|
||||||
editable=false,
|
|
||||||
tags=($._config.dashboardTags),
|
|
||||||
timezone='%s' % $._config.dashboardTimezone,
|
|
||||||
refresh='%s' % $._config.dashboardRefresh,
|
|
||||||
graphTooltip='shared_crosshair',
|
|
||||||
uid='gitea-overview'
|
|
||||||
)
|
|
||||||
.addTemplate(
|
|
||||||
{
|
|
||||||
current: {
|
|
||||||
text: 'Prometheus',
|
|
||||||
value: 'Prometheus',
|
|
||||||
},
|
|
||||||
hide: 0,
|
|
||||||
label: 'Data Source',
|
|
||||||
name: 'datasource',
|
|
||||||
options: [],
|
|
||||||
query: 'prometheus',
|
|
||||||
refresh: 1,
|
|
||||||
regex: '',
|
|
||||||
type: 'datasource',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.addTemplate(
|
|
||||||
{
|
|
||||||
hide: 0,
|
|
||||||
label: null,
|
|
||||||
name: 'job',
|
|
||||||
options: [],
|
|
||||||
query: 'label_values(gitea_organizations, job)',
|
|
||||||
refresh: 1,
|
|
||||||
regex: '',
|
|
||||||
type: 'query',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.addTemplate(
|
|
||||||
{
|
|
||||||
hide: 0,
|
|
||||||
label: null,
|
|
||||||
name: 'instance',
|
|
||||||
options: [],
|
|
||||||
query: 'label_values(gitea_organizations{job="$job"}, instance)',
|
|
||||||
refresh: 1,
|
|
||||||
regex: '',
|
|
||||||
type: 'query',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.addTemplate(
|
|
||||||
{
|
|
||||||
hide: 0,
|
|
||||||
label: 'aggregation interval',
|
|
||||||
name: 'agg_interval',
|
|
||||||
auto_min: '1m',
|
|
||||||
auto: true,
|
|
||||||
query: '1m,10m,1h,1d,7d',
|
|
||||||
type: 'interval',
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.addPanel(grafana.row.new(title='General'), gridPos={ x: 0, y: 0, w: 0, h: 0 },)
|
|
||||||
.addPanel(giteaStatsPanel, gridPos={ x: 0, y: 0, w: 16, h: 4 })
|
|
||||||
.addPanel(giteaUptimePanel, gridPos={ x: 16, y: 0, w: 8, h: 4 })
|
|
||||||
.addPanel(giteaMemoryPanel, gridPos={ x: 0, y: 4, w: 8, h: 6 })
|
|
||||||
.addPanel(giteaCpuPanel, gridPos={ x: 8, y: 4, w: 8, h: 6 })
|
|
||||||
.addPanel(giteaFileDescriptorsPanel, gridPos={ x: 16, y: 4, w: 8, h: 6 })
|
|
||||||
.addPanel(grafana.row.new(title='Changes', collapse=false), gridPos={ x: 0, y: 10, w: 24, h: 8 })
|
|
||||||
.addPanel(giteaChangesPanelTotal, gridPos={ x: 0, y: 12, w: 6, h: 8 })
|
|
||||||
+ // use patching instead of .addPanel() to keep static ids
|
|
||||||
{
|
|
||||||
panels+: std.flattenArrays([
|
|
||||||
[
|
|
||||||
giteaChangesPanelAll { gridPos: { x: 6, y: 12, w: 18, h: 8 } },
|
|
||||||
],
|
|
||||||
if $._config.showIssuesByRepository then
|
|
||||||
[
|
|
||||||
giteaChangesByRepositoriesTotal { gridPos: { x: 0, y: 20, w: 6, h: 8 } },
|
|
||||||
giteaChangesByRepositories { gridPos: { x: 6, y: 20, w: 18, h: 8 } },
|
|
||||||
] else [],
|
|
||||||
if $._config.showIssuesByLabel then
|
|
||||||
[
|
|
||||||
giteaChangesByLabelTotal { gridPos: { x: 0, y: 28, w: 6, h: 8 } },
|
|
||||||
giteaChangesByLabel { gridPos: { x: 6, y: 28, w: 18, h: 8 } },
|
|
||||||
] else [],
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"dependencies": [
|
|
||||||
{
|
|
||||||
"source": {
|
|
||||||
"git": {
|
|
||||||
"remote": "https://github.com/grafana/grafonnet-lib.git",
|
|
||||||
"subdir": "grafonnet"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"version": "master"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"legacyImports": false
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"dependencies": [
|
|
||||||
{
|
|
||||||
"source": {
|
|
||||||
"git": {
|
|
||||||
"remote": "https://github.com/grafana/grafonnet-lib.git",
|
|
||||||
"subdir": "grafonnet"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"version": "3626fc4dc2326931c530861ac5bebe39444f6cbf",
|
|
||||||
"sum": "gF8foHByYcB25jcUOBqP6jxk0OPifQMjPvKY0HaCk6w="
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"legacyImports": false
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
std.manifestYamlDoc((import '../mixin.libsonnet').prometheusAlerts)
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
local dashboards = (import '../mixin.libsonnet').grafanaDashboards;
|
|
||||||
|
|
||||||
{
|
|
||||||
[name]: dashboards[name]
|
|
||||||
for name in std.objectFields(dashboards)
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
std.manifestYamlDoc((import '../mixin.libsonnet').prometheusRules)
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
(import 'dashboards/dashboards.libsonnet') +
|
|
||||||
(import 'config.libsonnet')
|
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
"type": "go",
|
"type": "go",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mode": "debug",
|
"mode": "debug",
|
||||||
"buildFlags": "-tags='sqlite sqlite_unlock_notify'",
|
"buildFlags": "-tags=\"sqlite sqlite_unlock_notify\"",
|
||||||
"port": 2345,
|
"port": 2345,
|
||||||
"host": "127.0.0.1",
|
"host": "127.0.0.1",
|
||||||
"program": "${workspaceRoot}/main.go",
|
"program": "${workspaceRoot}/main.go",
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"go.buildTags": "'sqlite sqlite_unlock_notify'",
|
|
||||||
"go.testFlags": ["-v"]
|
|
||||||
}
|
|
||||||
@@ -12,14 +12,15 @@
|
|||||||
"focus": false,
|
"focus": false,
|
||||||
"panel": "shared"
|
"panel": "shared"
|
||||||
},
|
},
|
||||||
|
"args": ["build"],
|
||||||
"linux": {
|
"linux": {
|
||||||
"args": ["build", "-o", "gitea", "${workspaceRoot}/main.go" ]
|
"args": [ "-o", "gitea", "${workspaceRoot}/main.go" ]
|
||||||
},
|
},
|
||||||
"osx": {
|
"osx": {
|
||||||
"args": ["build", "-o", "gitea", "${workspaceRoot}/main.go" ]
|
"args": [ "-o", "gitea", "${workspaceRoot}/main.go" ]
|
||||||
},
|
},
|
||||||
"windows": {
|
"windows": {
|
||||||
"args": ["build", "-o", "gitea.exe", "\"${workspaceRoot}\\main.go\""]
|
"args": [ "-o", "gitea.exe", "\"${workspaceRoot}\\main.go\""]
|
||||||
},
|
},
|
||||||
"problemMatcher": ["$go"]
|
"problemMatcher": ["$go"]
|
||||||
},
|
},
|
||||||
@@ -34,14 +35,15 @@
|
|||||||
"focus": false,
|
"focus": false,
|
||||||
"panel": "shared"
|
"panel": "shared"
|
||||||
},
|
},
|
||||||
|
"args": ["build", "-tags=\"sqlite sqlite_unlock_notify\""],
|
||||||
"linux": {
|
"linux": {
|
||||||
"args": ["build", "-tags=\"sqlite sqlite_unlock_notify\"", "-o", "gitea", "${workspaceRoot}/main.go"]
|
"args": ["-o", "gitea", "${workspaceRoot}/main.go"]
|
||||||
},
|
},
|
||||||
"osx": {
|
"osx": {
|
||||||
"args": ["build", "-tags=\"sqlite sqlite_unlock_notify\"", "-o", "gitea", "${workspaceRoot}/main.go"]
|
"args": ["-o", "gitea", "${workspaceRoot}/main.go"]
|
||||||
},
|
},
|
||||||
"windows": {
|
"windows": {
|
||||||
"args": ["build", "-tags=\"sqlite sqlite_unlock_notify\"", "-o", "gitea.exe", "\"${workspaceRoot}\\main.go\""]
|
"args": ["-o", "gitea.exe", "\"${workspaceRoot}\\main.go\""]
|
||||||
},
|
},
|
||||||
"problemMatcher": ["$go"]
|
"problemMatcher": ["$go"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
|
|||||||
DESC="Gitea - Git with a cup of tea"
|
DESC="Gitea - Git with a cup of tea"
|
||||||
NAME=gitea
|
NAME=gitea
|
||||||
SERVICEVERBOSE=yes
|
SERVICEVERBOSE=yes
|
||||||
PIDFILE=/run/$NAME.pid
|
PIDFILE=/var/run/$NAME.pid
|
||||||
SCRIPTNAME=/etc/init.d/$NAME
|
SCRIPTNAME=/etc/init.d/$NAME
|
||||||
WORKINGDIR=/var/lib/$NAME
|
WORKINGDIR=/var/lib/$NAME
|
||||||
DAEMON=/usr/local/bin/$NAME
|
DAEMON=/usr/local/bin/$NAME
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ start_stop_daemon_args="--user ${USER} --chdir ${DIR}"
|
|||||||
command="/usr/local/bin/gitea"
|
command="/usr/local/bin/gitea"
|
||||||
command_args="web -c /etc/gitea/app.ini"
|
command_args="web -c /etc/gitea/app.ini"
|
||||||
command_background=yes
|
command_background=yes
|
||||||
pidfile=/run/gitea.pid
|
pidfile=/var/run/gitea.pid
|
||||||
|
|
||||||
depend()
|
depend()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ case "$1" in
|
|||||||
|
|
||||||
# Return value is slightly different for the status command:
|
# Return value is slightly different for the status command:
|
||||||
# 0 - service up and running
|
# 0 - service up and running
|
||||||
# 1 - service dead, but /run/ pid file exists
|
# 1 - service dead, but /var/run/ pid file exists
|
||||||
# 2 - service dead, but /var/lock/ lock file exists
|
# 2 - service dead, but /var/lock/ lock file exists
|
||||||
# 3 - service not running (unused)
|
# 3 - service not running (unused)
|
||||||
# 4 - service status unknown :-(
|
# 4 - service status unknown :-(
|
||||||
|
|||||||
107
contrib/k8s/gitea.yml
Normal file
107
contrib/k8s/gitea.yml
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: gitea
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: gitea
|
||||||
|
namespace: gitea
|
||||||
|
labels:
|
||||||
|
app: gitea
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
name: gitea
|
||||||
|
labels:
|
||||||
|
app: gitea
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: gitea
|
||||||
|
image: gitea/gitea:latest
|
||||||
|
imagePullPolicy: Always
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: "/var/lib/gitea"
|
||||||
|
name: "root"
|
||||||
|
- mountPath: "/data"
|
||||||
|
name: "data"
|
||||||
|
ports:
|
||||||
|
- containerPort: 22
|
||||||
|
name: ssh
|
||||||
|
protocol: TCP
|
||||||
|
- containerPort: 3000
|
||||||
|
name: http
|
||||||
|
protocol: TCP
|
||||||
|
restartPolicy: Always
|
||||||
|
volumes:
|
||||||
|
# Set up a data directory for gitea
|
||||||
|
# For production usage, you should consider using PV/PVC instead(or simply using storage like NAS)
|
||||||
|
# For more details, please see https://kubernetes.io/docs/concepts/storage/volumes/
|
||||||
|
- name: "root"
|
||||||
|
hostPath:
|
||||||
|
# directory location on host
|
||||||
|
path: "/var/lib/gitea"
|
||||||
|
# this field is optional
|
||||||
|
type: Directory
|
||||||
|
- name: "data"
|
||||||
|
hostPath:
|
||||||
|
path: "/data/gitea"
|
||||||
|
type: Directory
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: gitea
|
||||||
|
---
|
||||||
|
# Using cluster mode
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: gitea-web
|
||||||
|
namespace: gitea
|
||||||
|
labels:
|
||||||
|
app: gitea-web
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 3000
|
||||||
|
name: http
|
||||||
|
selector:
|
||||||
|
app: gitea
|
||||||
|
---
|
||||||
|
# Using node-port mode
|
||||||
|
# This mainly open a specific TCP port for SSH usage on each host,
|
||||||
|
# so you can use a proxy layer to handle it(e.g. slb, nginx)
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: gitea-ssh
|
||||||
|
namespace: gitea
|
||||||
|
labels:
|
||||||
|
app: gitea-ssh
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 22
|
||||||
|
targetPort: 22
|
||||||
|
nodePort: 30022
|
||||||
|
name: ssh
|
||||||
|
selector:
|
||||||
|
app: gitea
|
||||||
|
type: NodePort
|
||||||
|
---
|
||||||
|
# Ingress is always suitable for HTTP usage,
|
||||||
|
# we suggest using an proxy layer such as slb to send traffic to different ports.
|
||||||
|
# Usually 80/443 for web and 22 directly for SSH.
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: gitea
|
||||||
|
namespace: gitea
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: your-gitea-host.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
serviceName: gitea-web
|
||||||
|
servicePort: 80
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<h1>Privacy Policy</h1>
|
<h1>Privacy Policy</h1>
|
||||||
|
|
||||||
<h4>Last updated: January 29, 2020</h4>
|
<h4>Last updated: December 28, 2019</h4>
|
||||||
|
|
||||||
<h2>Who We Are?</h2>
|
<h2>Who We Are?</h2>
|
||||||
|
|
||||||
@@ -191,6 +191,6 @@
|
|||||||
|
|
||||||
<h2>COPYING</h2>
|
<h2>COPYING</h2>
|
||||||
|
|
||||||
<p>This document is licensed under CC0 Public Domain License. See <a href="https://creativecommons.org/publicdomain/zero/1.0/legalcode">full legal code here</a>.</p>
|
<p>This document is licensed under <a href="https://creativecommons.org/publicdomain/zero/1.0/">CC0 Public Domain license</a>.</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<h1>Terms of Service</h1>
|
<h1>Terms of Service</h1>
|
||||||
|
|
||||||
<h4>Last updated: January 29, 2020</h4>
|
<h4>Last updated: December 31, 2019</h4>
|
||||||
|
|
||||||
<p>Thank you for choosing Your Gitea Instance! Before you use it, please read this Terms of Service agreement carefully, which contains important contract between us and our users.</p>
|
<p>Thank you for choosing Your Gitea Instance! Before you use it, please read this Terms of Service agreement carefully, which contains important contract between us and our users.</p>
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
|
|
||||||
<p>If you'd like to use our trademarks, you must follow all of our trademark guidelines.</p>
|
<p>If you'd like to use our trademarks, you must follow all of our trademark guidelines.</p>
|
||||||
|
|
||||||
<p>This Agreement is licensed under <a href="https://creativecommons.org/publicdomain/zero/1.0/legalcode">CCO Public Domain License</a>.</p>
|
<p>This Agreement is licensed under <a href="https://creativecommons.org/publicdomain/zero/1.0/">CCO Public Domain License</a>.</p>
|
||||||
|
|
||||||
<h2>API Terms</h2>
|
<h2>API Terms</h2>
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -25,18 +26,18 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/models/db"
|
|
||||||
"code.gitea.io/gitea/models/unittest"
|
|
||||||
gitea_git "code.gitea.io/gitea/modules/git"
|
|
||||||
"code.gitea.io/gitea/modules/markup"
|
"code.gitea.io/gitea/modules/markup"
|
||||||
"code.gitea.io/gitea/modules/markup/external"
|
"code.gitea.io/gitea/modules/markup/external"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
"code.gitea.io/gitea/routers"
|
"code.gitea.io/gitea/routers"
|
||||||
|
"code.gitea.io/gitea/routers/routes"
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
"github.com/go-git/go-git/v5/config"
|
"github.com/go-git/go-git/v5/config"
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
"github.com/go-git/go-git/v5/plumbing"
|
||||||
|
context2 "github.com/gorilla/context"
|
||||||
|
"github.com/unknwon/com"
|
||||||
|
"gopkg.in/testfixtures.v2"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -49,13 +50,13 @@ func runPR() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
setting.SetCustomPathAndConf("", "", "")
|
setting.SetCustomPathAndConf("", "", "")
|
||||||
setting.LoadAllowEmpty()
|
setting.NewContext()
|
||||||
|
|
||||||
setting.RepoRootPath, err = os.MkdirTemp(os.TempDir(), "repos")
|
setting.RepoRootPath, err = ioutil.TempDir(os.TempDir(), "repos")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("TempDir: %v\n", err)
|
log.Fatalf("TempDir: %v\n", err)
|
||||||
}
|
}
|
||||||
setting.AppDataPath, err = os.MkdirTemp(os.TempDir(), "appdata")
|
setting.AppDataPath, err = ioutil.TempDir(os.TempDir(), "appdata")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("TempDir: %v\n", err)
|
log.Fatalf("TempDir: %v\n", err)
|
||||||
}
|
}
|
||||||
@@ -80,45 +81,46 @@ func runPR() {
|
|||||||
setting.RunUser = curUser.Username
|
setting.RunUser = curUser.Username
|
||||||
|
|
||||||
log.Printf("[PR] Loading fixtures data ...\n")
|
log.Printf("[PR] Loading fixtures data ...\n")
|
||||||
gitea_git.CheckLFSVersion()
|
setting.CheckLFSVersion()
|
||||||
//models.LoadConfigs()
|
//models.LoadConfigs()
|
||||||
/*
|
/*
|
||||||
setting.Database.Type = "sqlite3"
|
setting.Database.Type = "sqlite3"
|
||||||
setting.Database.Path = ":memory:"
|
setting.Database.Path = ":memory:"
|
||||||
setting.Database.Timeout = 500
|
setting.Database.Timeout = 500
|
||||||
*/
|
*/
|
||||||
dbCfg := setting.Cfg.Section("database")
|
db := setting.Cfg.Section("database")
|
||||||
dbCfg.NewKey("DB_TYPE", "sqlite3")
|
db.NewKey("DB_TYPE", "sqlite3")
|
||||||
dbCfg.NewKey("PATH", ":memory:")
|
db.NewKey("PATH", ":memory:")
|
||||||
|
|
||||||
routers.InitGitServices()
|
routers.NewServices()
|
||||||
setting.Database.LogSQL = true
|
setting.Database.LogSQL = true
|
||||||
//x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
|
//x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
|
||||||
|
|
||||||
db.InitEngineWithMigration(context.Background(), func(_ *xorm.Engine) error {
|
var helper testfixtures.Helper = &testfixtures.SQLite{}
|
||||||
|
models.NewEngine(context.Background(), func(_ *xorm.Engine) error {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
db.HasEngine = true
|
models.HasEngine = true
|
||||||
//x.ShowSQL(true)
|
//x.ShowSQL(true)
|
||||||
err = unittest.InitFixtures(
|
err = models.InitFixtures(
|
||||||
unittest.FixturesOptions{
|
helper,
|
||||||
Dir: path.Join(curDir, "models/fixtures/"),
|
path.Join(curDir, "models/fixtures/"),
|
||||||
},
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error initializing test database: %v\n", err)
|
fmt.Printf("Error initializing test database: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
unittest.LoadFixtures()
|
models.LoadFixtures()
|
||||||
util.RemoveAll(setting.RepoRootPath)
|
os.RemoveAll(setting.RepoRootPath)
|
||||||
util.RemoveAll(models.LocalCopyPath())
|
os.RemoveAll(models.LocalCopyPath())
|
||||||
util.CopyDir(path.Join(curDir, "integrations/gitea-repositories-meta"), setting.RepoRootPath)
|
com.CopyDir(path.Join(curDir, "integrations/gitea-repositories-meta"), setting.RepoRootPath)
|
||||||
|
|
||||||
log.Printf("[PR] Setting up router\n")
|
log.Printf("[PR] Setting up router\n")
|
||||||
//routers.GlobalInit()
|
//routers.GlobalInit()
|
||||||
external.RegisterRenderers()
|
external.RegisterParsers()
|
||||||
markup.Init()
|
markup.Init()
|
||||||
c := routers.NormalRoutes()
|
m := routes.NewMacaron()
|
||||||
|
routes.RegisterRoutes(m)
|
||||||
|
|
||||||
log.Printf("[PR] Ready for testing !\n")
|
log.Printf("[PR] Ready for testing !\n")
|
||||||
log.Printf("[PR] Login with user1, user2, user3, ... with pass: password\n")
|
log.Printf("[PR] Login with user1, user2, user3, ... with pass: password\n")
|
||||||
@@ -138,24 +140,24 @@ func runPR() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
//Start the server
|
//Start the server
|
||||||
http.ListenAndServe(":8080", c)
|
http.ListenAndServe(":8080", context2.ClearHandler(m))
|
||||||
|
|
||||||
log.Printf("[PR] Cleaning up ...\n")
|
log.Printf("[PR] Cleaning up ...\n")
|
||||||
/*
|
/*
|
||||||
if err = util.RemoveAll(setting.Indexer.IssuePath); err != nil {
|
if err = os.RemoveAll(setting.Indexer.IssuePath); err != nil {
|
||||||
fmt.Printf("util.RemoveAll: %v\n", err)
|
fmt.Printf("os.RemoveAll: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if err = util.RemoveAll(setting.Indexer.RepoPath); err != nil {
|
if err = os.RemoveAll(setting.Indexer.RepoPath); err != nil {
|
||||||
fmt.Printf("Unable to remove repo indexer: %v\n", err)
|
fmt.Printf("Unable to remove repo indexer: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
if err = util.RemoveAll(setting.RepoRootPath); err != nil {
|
if err = os.RemoveAll(setting.RepoRootPath); err != nil {
|
||||||
log.Fatalf("util.RemoveAll: %v\n", err)
|
log.Fatalf("os.RemoveAll: %v\n", err)
|
||||||
}
|
}
|
||||||
if err = util.RemoveAll(setting.AppDataPath); err != nil {
|
if err = os.RemoveAll(setting.AppDataPath); err != nil {
|
||||||
log.Fatalf("util.RemoveAll: %v\n", err)
|
log.Fatalf("os.RemoveAll: %v\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +184,7 @@ func main() {
|
|||||||
codeFilePath = filepath.FromSlash(codeFilePath) //Convert to running OS
|
codeFilePath = filepath.FromSlash(codeFilePath) //Convert to running OS
|
||||||
|
|
||||||
//Copy this file if it will not exist in the PR branch
|
//Copy this file if it will not exist in the PR branch
|
||||||
dat, err := os.ReadFile(codeFilePath)
|
dat, err := ioutil.ReadFile(codeFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to cache this code file : %v", err)
|
log.Fatalf("Failed to cache this code file : %v", err)
|
||||||
}
|
}
|
||||||
@@ -199,9 +201,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
remoteUpstream := "origin" //Default
|
remoteUpstream := "origin" //Default
|
||||||
for _, r := range remotes {
|
for _, r := range remotes {
|
||||||
if r.Config().URLs[0] == "https://github.com/go-gitea/gitea.git" ||
|
if r.Config().URLs[0] == "https://github.com/go-gitea/gitea" || r.Config().URLs[0] == "git@github.com:go-gitea/gitea.git" { //fetch at index 0
|
||||||
r.Config().URLs[0] == "https://github.com/go-gitea/gitea" ||
|
|
||||||
r.Config().URLs[0] == "git@github.com:go-gitea/gitea.git" { //fetch at index 0
|
|
||||||
remoteUpstream = r.Config().Name
|
remoteUpstream = r.Config().Name
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -215,7 +215,7 @@ func main() {
|
|||||||
//Use git cli command for windows
|
//Use git cli command for windows
|
||||||
runCmd("git", "fetch", remoteUpstream, fmt.Sprintf("pull/%s/head:%s", pr, branch))
|
runCmd("git", "fetch", remoteUpstream, fmt.Sprintf("pull/%s/head:%s", pr, branch))
|
||||||
} else {
|
} else {
|
||||||
ref := fmt.Sprintf("%s%s/head:%s", gitea_git.PullPrefix, pr, branchRef)
|
ref := fmt.Sprintf("refs/pull/%s/head:%s", pr, branchRef)
|
||||||
err = repo.Fetch(&git.FetchOptions{
|
err = repo.Fetch(&git.FetchOptions{
|
||||||
RemoteName: remoteUpstream,
|
RemoteName: remoteUpstream,
|
||||||
RefSpecs: []config.RefSpec{
|
RefSpecs: []config.RefSpec{
|
||||||
@@ -246,7 +246,7 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to duplicate this code file in PR : %v", err)
|
log.Fatalf("Failed to duplicate this code file in PR : %v", err)
|
||||||
}
|
}
|
||||||
err = os.WriteFile(codeFilePath, dat, 0644)
|
err = ioutil.WriteFile(codeFilePath, dat, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to duplicate this code file in PR : %v", err)
|
log.Fatalf("Failed to duplicate this code file in PR : %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,23 +3,14 @@ Description=Gitea (Git with a cup of tea)
|
|||||||
After=syslog.target
|
After=syslog.target
|
||||||
After=network.target
|
After=network.target
|
||||||
###
|
###
|
||||||
# Don't forget to add the database service dependencies
|
# Don't forget to add the database service requirements
|
||||||
###
|
###
|
||||||
#
|
#
|
||||||
#Wants=mysql.service
|
#Requires=mysql.service
|
||||||
#After=mysql.service
|
#Requires=mariadb.service
|
||||||
#
|
#Requires=postgresql.service
|
||||||
#Wants=mariadb.service
|
#Requires=memcached.service
|
||||||
#After=mariadb.service
|
#Requires=redis.service
|
||||||
#
|
|
||||||
#Wants=postgresql.service
|
|
||||||
#After=postgresql.service
|
|
||||||
#
|
|
||||||
#Wants=memcached.service
|
|
||||||
#After=memcached.service
|
|
||||||
#
|
|
||||||
#Wants=redis.service
|
|
||||||
#After=redis.service
|
|
||||||
#
|
#
|
||||||
###
|
###
|
||||||
# If using socket activation for main http/s
|
# If using socket activation for main http/s
|
||||||
@@ -66,12 +57,6 @@ WorkingDirectory=/var/lib/gitea/
|
|||||||
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
|
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
|
||||||
Restart=always
|
Restart=always
|
||||||
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
|
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
|
||||||
# If you install Git to directory prefix other than default PATH (which happens
|
|
||||||
# for example if you install other versions of Git side-to-side with
|
|
||||||
# distribution version), uncomment below line and add that prefix to PATH
|
|
||||||
# Don't forget to place git-lfs binary on the PATH below if you want to enable
|
|
||||||
# Git LFS support
|
|
||||||
#Environment=PATH=/path/to/git/bin:/bin:/sbin:/usr/bin:/usr/sbin
|
|
||||||
# If you want to bind Gitea to a port below 1024, uncomment
|
# If you want to bind Gitea to a port below 1024, uncomment
|
||||||
# the two values below, or use socket activation to pass Gitea its ports as above
|
# the two values below, or use socket activation to pass Gitea its ports as above
|
||||||
###
|
###
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
grep 'git' go.mod | grep '\.com' | grep -v indirect | grep -v replace | cut -f 2 | cut -d ' ' -f 1 | while read line; do
|
|
||||||
go get -u "$line"
|
|
||||||
make vendor
|
|
||||||
git add .
|
|
||||||
git commit -S -m "update $line"
|
|
||||||
done
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user