mirror of
https://github.com/go-gitea/gitea.git
synced 2025-08-09 20:11:44 +02:00
Compare commits
84 Commits
release/v1
...
release/v1
Author | SHA1 | Date | |
---|---|---|---|
|
11f6ed4f83 | ||
|
e94a84248d | ||
|
cf7a5b3d91 | ||
|
5d1a8d23b0 | ||
|
dbd0a2e6dc | ||
|
7697a282d6 | ||
|
76e8eec3d9 | ||
|
10effb396a | ||
|
5e97b2d00e | ||
|
873acd884d | ||
|
dc73b2748d | ||
|
31ad8b7026 | ||
|
d07edc5336 | ||
|
63cb160cb1 | ||
|
8d5c3d3d0b | ||
|
706d85b87d | ||
|
75e491c03e | ||
|
608f46e59c | ||
|
895764e7f5 | ||
|
21983965d0 | ||
|
e069a75817 | ||
|
ebb8fa610c | ||
|
c8fc7fce4a | ||
|
57b226e284 | ||
|
5f20841bc3 | ||
|
40dc7342cf | ||
|
6d2f0e555e | ||
|
476ca67cd4 | ||
|
96eb99ea55 | ||
|
83560bf9d0 | ||
|
6b29a6b6f2 | ||
|
74eee64b57 | ||
|
d21dabab4e | ||
|
34ba1c8957 | ||
|
98d82a7dd9 | ||
|
ee163a8af1 | ||
|
ed96b84b2e | ||
|
7284829962 | ||
|
21fb791747 | ||
|
8b3aad940e | ||
|
81adf6ad86 | ||
|
e6cd4f3276 | ||
|
30226b4793 | ||
|
aecd9231ba | ||
|
26c1550643 | ||
|
5f6b118007 | ||
|
b1cb52e477 | ||
|
799f5e05c9 | ||
|
497f37bffd | ||
|
d7aa553f1b | ||
|
ba12463175 | ||
|
0acaa6bd00 | ||
|
66a3353c31 | ||
|
5236d8a936 | ||
|
5876e37ed4 | ||
|
3c21a0ee80 | ||
|
f43783f003 | ||
|
b9c5a3acc3 | ||
|
c363ef5da0 | ||
|
e8ca2da08f | ||
|
f64b8eb009 | ||
|
40f41dc694 | ||
|
a63b9fbc70 | ||
|
4b87aa367c | ||
|
72f4cdf868 | ||
|
5be1b7df3f | ||
|
95e12be30f | ||
|
245089b9c9 | ||
|
2551660f49 | ||
|
3b28de7d8e | ||
|
3725eefb7f | ||
|
73ce02400c | ||
|
197cbd674d | ||
|
4a0f7c1eb4 | ||
|
e54f7a708c | ||
|
63f6764dce | ||
|
0bf7ed55be | ||
|
93e8174e4e | ||
|
c5ec66a8a3 | ||
|
b6fb082b78 | ||
|
3ce195115b | ||
|
00619a04f7 | ||
|
16815306ad | ||
|
3934d9cd2f |
@@ -1,9 +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"]
|
@@ -1,61 +1,44 @@
|
||||
# The full repository name
|
||||
repo: go-gitea/gitea
|
||||
|
||||
# Service type (gitea or github)
|
||||
service: github
|
||||
|
||||
# Base URL for Gitea instance if using gitea service type (optional)
|
||||
# Default: https://gitea.com
|
||||
base-url:
|
||||
|
||||
# Changelog groups and which labeled PRs to add to each group
|
||||
groups:
|
||||
-
|
||||
-
|
||||
name: BREAKING
|
||||
labels:
|
||||
- kind/breaking
|
||||
-
|
||||
name: FEATURES
|
||||
-
|
||||
name: FEATURE
|
||||
labels:
|
||||
- kind/feature
|
||||
-
|
||||
name: SECURITY
|
||||
labels:
|
||||
- kind/security
|
||||
-
|
||||
name: API
|
||||
labels:
|
||||
- kind/api
|
||||
-
|
||||
name: BUGFIXES
|
||||
labels:
|
||||
- kind/bug
|
||||
-
|
||||
name: ENHANCEMENTS
|
||||
-
|
||||
name: ENHANCEMENT
|
||||
labels:
|
||||
- kind/enhancement
|
||||
- kind/refactor
|
||||
- kind/ui
|
||||
-
|
||||
name: SECURITY
|
||||
labels:
|
||||
- kind/security
|
||||
-
|
||||
name: TESTING
|
||||
labels:
|
||||
- kind/testing
|
||||
-
|
||||
-
|
||||
name: TRANSLATION
|
||||
labels:
|
||||
- kind/translation
|
||||
-
|
||||
-
|
||||
name: BUILD
|
||||
labels:
|
||||
- kind/build
|
||||
- kind/lint
|
||||
-
|
||||
-
|
||||
name: DOCS
|
||||
labels:
|
||||
- kind/docs
|
||||
-
|
||||
-
|
||||
name: MISC
|
||||
default: true
|
||||
|
||||
# regex indicating which labels to skip for the changelog
|
||||
skip-labels: skip-changelog|backport\/.+
|
||||
default: true
|
1198
.drone.yml
1198
.drone.yml
File diff suppressed because it is too large
Load Diff
@@ -1,22 +1,30 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
|
||||
[*.{tmpl,html}]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.less]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{yml,json}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{go,tmpl,html}]
|
||||
indent_style = tab
|
||||
[*.js]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
[*.svg]
|
||||
insert_final_newline = false
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
389
.eslintrc
389
.eslintrc
@@ -1,389 +0,0 @@
|
||||
root: true
|
||||
reportUnusedDisableDirectives: true
|
||||
|
||||
ignorePatterns:
|
||||
- /web_src/js/vendor
|
||||
|
||||
parserOptions:
|
||||
sourceType: module
|
||||
ecmaVersion: 2020
|
||||
|
||||
plugins:
|
||||
- eslint-plugin-unicorn
|
||||
- eslint-plugin-import
|
||||
|
||||
env:
|
||||
es2021: true
|
||||
node: true
|
||||
|
||||
globals:
|
||||
__webpack_public_path__: true
|
||||
CodeMirror: false
|
||||
Dropzone: false
|
||||
SimpleMDE: false
|
||||
u2fApi: false
|
||||
|
||||
overrides:
|
||||
- files: ["web_src/**/*.js"]
|
||||
env:
|
||||
browser: true
|
||||
jquery: true
|
||||
node: false
|
||||
- files: ["web_src/**/*worker.js"]
|
||||
env:
|
||||
worker: true
|
||||
rules:
|
||||
no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, location, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, status, statusbar, stop, toolbar, top]
|
||||
- files: ["build/generate-images.js"]
|
||||
rules:
|
||||
import/no-unresolved: [0]
|
||||
import/no-extraneous-dependencies: [0]
|
||||
|
||||
rules:
|
||||
accessor-pairs: [2]
|
||||
array-bracket-newline: [0]
|
||||
array-bracket-spacing: [2, never]
|
||||
array-callback-return: [0]
|
||||
array-element-newline: [0]
|
||||
arrow-body-style: [0]
|
||||
arrow-parens: [2, always]
|
||||
arrow-spacing: [2, {before: true, after: true}]
|
||||
block-scoped-var: [2]
|
||||
brace-style: [2, 1tbs, {allowSingleLine: true}]
|
||||
camelcase: [0]
|
||||
capitalized-comments: [0]
|
||||
class-methods-use-this: [0]
|
||||
comma-dangle: [2, only-multiline]
|
||||
comma-spacing: [2, {before: false, after: true}]
|
||||
comma-style: [2, last]
|
||||
complexity: [0]
|
||||
computed-property-spacing: [2, never]
|
||||
consistent-return: [0]
|
||||
consistent-this: [0]
|
||||
constructor-super: [2]
|
||||
curly: [0]
|
||||
default-case-last: [2]
|
||||
default-case: [0]
|
||||
default-param-last: [0]
|
||||
dot-location: [2, property]
|
||||
dot-notation: [0]
|
||||
eol-last: [2]
|
||||
eqeqeq: [2]
|
||||
for-direction: [2]
|
||||
func-call-spacing: [2, never]
|
||||
func-name-matching: [2]
|
||||
func-names: [0]
|
||||
func-style: [0]
|
||||
function-call-argument-newline: [0]
|
||||
function-paren-newline: [0]
|
||||
generator-star-spacing: [0]
|
||||
getter-return: [2]
|
||||
grouped-accessor-pairs: [2]
|
||||
guard-for-in: [0]
|
||||
id-blacklist: [0]
|
||||
id-length: [0]
|
||||
id-match: [0]
|
||||
implicit-arrow-linebreak: [0]
|
||||
import/default: [0]
|
||||
import/dynamic-import-chunkname: [0]
|
||||
import/export: [2]
|
||||
import/exports-last: [0]
|
||||
import/extensions: [2, always, {ignorePackages: true}]
|
||||
import/first: [2]
|
||||
import/group-exports: [0]
|
||||
import/max-dependencies: [0]
|
||||
import/named: [2]
|
||||
import/namespace: [0]
|
||||
import/newline-after-import: [0]
|
||||
import/no-absolute-path: [0]
|
||||
import/no-amd: [0]
|
||||
import/no-anonymous-default-export: [0]
|
||||
import/no-commonjs: [0]
|
||||
import/no-cycle: [0]
|
||||
import/no-default-export: [0]
|
||||
import/no-deprecated: [0]
|
||||
import/no-dynamic-require: [0]
|
||||
import/no-extraneous-dependencies: [2]
|
||||
import/no-internal-modules: [0]
|
||||
import/no-mutable-exports: [2]
|
||||
import/no-named-as-default-member: [0]
|
||||
import/no-named-as-default: [2]
|
||||
import/no-named-default: [0]
|
||||
import/no-named-export: [0]
|
||||
import/no-namespace: [0]
|
||||
import/no-nodejs-modules: [0]
|
||||
import/no-relative-parent-imports: [0]
|
||||
import/no-restricted-paths: [0]
|
||||
import/no-self-import: [2]
|
||||
import/no-unassigned-import: [0]
|
||||
import/no-unresolved: [2, {commonjs: true}]
|
||||
import/no-unused-modules: [0]
|
||||
import/no-useless-path-segments: [2, {commonjs: true}]
|
||||
import/no-webpack-loader-syntax: [2]
|
||||
import/order: [0]
|
||||
import/prefer-default-export: [0]
|
||||
import/unambiguous: [0]
|
||||
indent: [2, 2, {SwitchCase: 1}]
|
||||
init-declarations: [0]
|
||||
key-spacing: [2]
|
||||
keyword-spacing: [2]
|
||||
line-comment-position: [0]
|
||||
linebreak-style: [2, unix]
|
||||
lines-around-comment: [0]
|
||||
lines-between-class-members: [0]
|
||||
max-classes-per-file: [0]
|
||||
max-depth: [0]
|
||||
max-len: [0]
|
||||
max-lines-per-function: [0]
|
||||
max-lines: [0]
|
||||
max-nested-callbacks: [0]
|
||||
max-params: [0]
|
||||
max-statements-per-line: [0]
|
||||
max-statements: [0]
|
||||
multiline-comment-style: [2, separate-lines]
|
||||
multiline-ternary: [0]
|
||||
new-cap: [0]
|
||||
new-parens: [2]
|
||||
newline-per-chained-call: [0]
|
||||
no-alert: [0]
|
||||
no-array-constructor: [2]
|
||||
no-async-promise-executor: [2]
|
||||
no-await-in-loop: [0]
|
||||
no-bitwise: [0]
|
||||
no-buffer-constructor: [0]
|
||||
no-caller: [2]
|
||||
no-case-declarations: [2]
|
||||
no-class-assign: [2]
|
||||
no-compare-neg-zero: [2]
|
||||
no-cond-assign: [2, except-parens]
|
||||
no-confusing-arrow: [0]
|
||||
no-console: [1, {allow: [info, warn, error]}]
|
||||
no-const-assign: [2]
|
||||
no-constant-condition: [0]
|
||||
no-constructor-return: [2]
|
||||
no-continue: [0]
|
||||
no-control-regex: [0]
|
||||
no-debugger: [1]
|
||||
no-delete-var: [2]
|
||||
no-div-regex: [0]
|
||||
no-dupe-args: [2]
|
||||
no-dupe-class-members: [2]
|
||||
no-dupe-else-if: [2]
|
||||
no-dupe-keys: [2]
|
||||
no-duplicate-case: [2]
|
||||
no-duplicate-imports: [2]
|
||||
no-else-return: [2]
|
||||
no-empty-character-class: [2]
|
||||
no-empty-function: [0]
|
||||
no-empty-pattern: [2]
|
||||
no-empty: [2, {allowEmptyCatch: true}]
|
||||
no-eq-null: [2]
|
||||
no-eval: [2]
|
||||
no-ex-assign: [2]
|
||||
no-extend-native: [2]
|
||||
no-extra-bind: [2]
|
||||
no-extra-boolean-cast: [2]
|
||||
no-extra-label: [0]
|
||||
no-extra-parens: [0]
|
||||
no-extra-semi: [2]
|
||||
no-fallthrough: [2]
|
||||
no-floating-decimal: [0]
|
||||
no-func-assign: [2]
|
||||
no-global-assign: [2]
|
||||
no-implicit-coercion: [0]
|
||||
no-implicit-globals: [0]
|
||||
no-implied-eval: [2]
|
||||
no-import-assign: [2]
|
||||
no-inline-comments: [0]
|
||||
no-inner-declarations: [2]
|
||||
no-invalid-regexp: [2]
|
||||
no-invalid-this: [0]
|
||||
no-irregular-whitespace: [2]
|
||||
no-iterator: [2]
|
||||
no-label-var: [2]
|
||||
no-labels: [2]
|
||||
no-lone-blocks: [2]
|
||||
no-lonely-if: [0]
|
||||
no-loop-func: [0]
|
||||
no-loss-of-precision: [2]
|
||||
no-magic-numbers: [0]
|
||||
no-misleading-character-class: [2]
|
||||
no-mixed-operators: [0]
|
||||
no-mixed-spaces-and-tabs: [2]
|
||||
no-multi-assign: [0]
|
||||
no-multi-spaces: [2, {ignoreEOLComments: true, exceptions: {Property: true, VariableDeclarator: 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-obj-calls: [2]
|
||||
no-octal-escape: [2]
|
||||
no-octal: [2]
|
||||
no-param-reassign: [0]
|
||||
no-plusplus: [0]
|
||||
no-promise-executor-return: [0]
|
||||
no-proto: [2]
|
||||
no-prototype-builtins: [2]
|
||||
no-redeclare: [2]
|
||||
no-regex-spaces: [2]
|
||||
no-restricted-exports: [0]
|
||||
no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, location, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, self, status, statusbar, stop, toolbar, top]
|
||||
no-restricted-imports: [0]
|
||||
no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement]
|
||||
no-return-assign: [0]
|
||||
no-return-await: [0]
|
||||
no-script-url: [2]
|
||||
no-self-assign: [2, {props: true}]
|
||||
no-self-compare: [2]
|
||||
no-sequences: [2]
|
||||
no-setter-return: [2]
|
||||
no-shadow-restricted-names: [2]
|
||||
no-shadow: [0]
|
||||
no-sparse-arrays: [2]
|
||||
no-tabs: [2]
|
||||
no-template-curly-in-string: [2]
|
||||
no-ternary: [0]
|
||||
no-this-before-super: [2]
|
||||
no-throw-literal: [2]
|
||||
no-trailing-spaces: [2]
|
||||
no-undef-init: [2]
|
||||
no-undef: [2, {typeof: true}]
|
||||
no-undefined: [0]
|
||||
no-underscore-dangle: [0]
|
||||
no-unexpected-multiline: [2]
|
||||
no-unmodified-loop-condition: [2]
|
||||
no-unneeded-ternary: [0]
|
||||
no-unreachable-loop: [2]
|
||||
no-unreachable: [2]
|
||||
no-unsafe-finally: [2]
|
||||
no-unsafe-negation: [2]
|
||||
no-unused-expressions: [2]
|
||||
no-unused-labels: [2]
|
||||
no-unused-vars: [2, {args: all, argsIgnorePattern: ^_, varsIgnorePattern: ^_, caughtErrorsIgnorePattern: ^_, ignoreRestSiblings: false}]
|
||||
no-use-before-define: [2, nofunc]
|
||||
no-useless-backreference: [0]
|
||||
no-useless-call: [2]
|
||||
no-useless-catch: [2]
|
||||
no-useless-computed-key: [2]
|
||||
no-useless-concat: [2]
|
||||
no-useless-constructor: [2]
|
||||
no-useless-escape: [2]
|
||||
no-useless-rename: [2]
|
||||
no-useless-return: [2]
|
||||
no-var: [2]
|
||||
no-void: [2]
|
||||
no-warning-comments: [0]
|
||||
no-whitespace-before-property: [2]
|
||||
no-with: [2]
|
||||
nonblock-statement-body-position: [2]
|
||||
object-curly-newline: [0]
|
||||
object-curly-spacing: [2, never]
|
||||
object-shorthand: [2, always]
|
||||
one-var-declaration-per-line: [0]
|
||||
one-var: [0]
|
||||
operator-assignment: [2, always]
|
||||
operator-linebreak: [2, after]
|
||||
padded-blocks: [2, never]
|
||||
padding-line-between-statements: [0]
|
||||
prefer-arrow-callback: [2, {allowNamedFunctions: true, allowUnboundThis: true}]
|
||||
prefer-const: [2, {destructuring: all}]
|
||||
prefer-destructuring: [0]
|
||||
prefer-exponentiation-operator: [2]
|
||||
prefer-named-capture-group: [0]
|
||||
prefer-numeric-literals: [2]
|
||||
prefer-object-spread: [0]
|
||||
prefer-promise-reject-errors: [2, {allowEmptyReject: false}]
|
||||
prefer-regex-literals: [2]
|
||||
prefer-rest-params: [2]
|
||||
prefer-spread: [2]
|
||||
prefer-template: [2]
|
||||
quote-props: [0]
|
||||
quotes: [2, single, {avoidEscape: true, allowTemplateLiterals: true}]
|
||||
radix: [2, as-needed]
|
||||
require-atomic-updates: [0]
|
||||
require-await: [0]
|
||||
require-unicode-regexp: [0]
|
||||
require-yield: [2]
|
||||
rest-spread-spacing: [2, never]
|
||||
semi-spacing: [2, {before: false, after: true}]
|
||||
semi-style: [2, last]
|
||||
semi: [2, always, {omitLastInOneLineBlock: true}]
|
||||
sort-imports: [0]
|
||||
sort-keys: [0]
|
||||
sort-vars: [0]
|
||||
space-before-blocks: [2, always]
|
||||
space-in-parens: [2, never]
|
||||
space-infix-ops: [2]
|
||||
space-unary-ops: [2]
|
||||
spaced-comment: [2, always]
|
||||
strict: [0]
|
||||
switch-colon-spacing: [2]
|
||||
symbol-description: [2]
|
||||
template-curly-spacing: [2, never]
|
||||
template-tag-spacing: [2, never]
|
||||
unicode-bom: [2, never]
|
||||
unicorn/better-regex: [0]
|
||||
unicorn/catch-error-name: [0]
|
||||
unicorn/consistent-function-scoping: [2]
|
||||
unicorn/custom-error-definition: [0]
|
||||
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/new-for-builtins: [2]
|
||||
unicorn/no-abusive-eslint-disable: [0]
|
||||
unicorn/no-array-instanceof: [0]
|
||||
unicorn/no-console-spaces: [0]
|
||||
unicorn/no-fn-reference-in-iterator: [0]
|
||||
unicorn/no-for-loop: [0]
|
||||
unicorn/no-hex-escape: [0]
|
||||
unicorn/no-keyword-prefix: [0]
|
||||
unicorn/no-nested-ternary: [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-unreadable-array-destructuring: [0]
|
||||
unicorn/no-unsafe-regex: [0]
|
||||
unicorn/no-unused-properties: [2]
|
||||
unicorn/no-useless-undefined: [0]
|
||||
unicorn/no-zero-fractions: [2]
|
||||
unicorn/number-literal-case: [0]
|
||||
unicorn/prefer-add-event-listener: [2]
|
||||
unicorn/prefer-array-find: [2]
|
||||
unicorn/prefer-dataset: [2]
|
||||
unicorn/prefer-event-key: [2]
|
||||
unicorn/prefer-includes: [2]
|
||||
unicorn/prefer-modern-dom-apis: [0]
|
||||
unicorn/prefer-negative-index: [2]
|
||||
unicorn/prefer-node-append: [0]
|
||||
unicorn/prefer-node-remove: [0]
|
||||
unicorn/prefer-number-properties: [0]
|
||||
unicorn/prefer-optional-catch-binding: [2]
|
||||
unicorn/prefer-query-selector: [0]
|
||||
unicorn/prefer-reflect-apply: [0]
|
||||
unicorn/prefer-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-text-content: [2]
|
||||
unicorn/prefer-trim-start-end: [2]
|
||||
unicorn/prefer-type-error: [0]
|
||||
unicorn/prevent-abbreviations: [0]
|
||||
unicorn/string-content: [0]
|
||||
unicorn/throw-new-error: [2]
|
||||
use-isnan: [2]
|
||||
valid-typeof: [2, {requireStringLiterals: true}]
|
||||
vars-on-top: [0]
|
||||
wrap-iife: [2, inside]
|
||||
wrap-regex: [0]
|
||||
yield-star-spacing: [2, after]
|
||||
yoda: [2, never]
|
9
.gitattributes
vendored
9
.gitattributes
vendored
@@ -1,3 +1,6 @@
|
||||
* text=auto eol=lf
|
||||
/vendor/** -text -eol linguist-vendored
|
||||
/public/vendor/** -text -eol linguist-vendored
|
||||
conf/* linguist-vendored
|
||||
docker/* linguist-vendored
|
||||
options/* linguist-vendored
|
||||
public/* linguist-vendored
|
||||
scripts/* linguist-vendored
|
||||
templates/* linguist-vendored
|
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@@ -1 +0,0 @@
|
||||
open_collective: gitea
|
9
.github/stale.yml
vendored
9
.github/stale.yml
vendored
@@ -27,11 +27,9 @@ staleLabel: stale
|
||||
|
||||
# Comment to post when marking as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had recent activity.
|
||||
I am here to help clear issues left open even if solved or waiting for more insight.
|
||||
This issue will be closed if no further activity occurs during the next 2 weeks.
|
||||
If the issue is still valid just add a comment to keep it alive.
|
||||
Thank you for your contributions.
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs during the next 2 weeks. Thank you
|
||||
for your contributions.
|
||||
|
||||
# Comment to post when closing a stale Issue or Pull Request.
|
||||
closeComment: >
|
||||
@@ -52,3 +50,4 @@ pulls:
|
||||
closeComment: >
|
||||
This pull request has been automatically closed because of inactivity.
|
||||
You can re-open it if needed.
|
||||
|
||||
|
26
.gitignore
vendored
26
.gitignore
vendored
@@ -12,7 +12,6 @@ _test
|
||||
|
||||
# MS VSCode
|
||||
.vscode
|
||||
__debug_bin
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
@@ -34,26 +33,19 @@ _testmain.go
|
||||
coverage.all
|
||||
|
||||
/modules/options/bindata.go
|
||||
/modules/options/bindata.go.hash
|
||||
/modules/public/bindata.go
|
||||
/modules/public/bindata.go.hash
|
||||
/modules/templates/bindata.go
|
||||
/modules/templates/bindata.go.hash
|
||||
|
||||
*.db
|
||||
*.log
|
||||
|
||||
/gitea
|
||||
/gitea-vet
|
||||
/debug
|
||||
/integrations.test
|
||||
|
||||
/bin
|
||||
/dist
|
||||
/custom/*
|
||||
!/custom/conf
|
||||
/custom/conf/*
|
||||
!/custom/conf/app.example.ini
|
||||
/custom
|
||||
/data
|
||||
/indexers
|
||||
/log
|
||||
@@ -68,21 +60,13 @@ coverage.all
|
||||
/integrations/indexers-pgsql
|
||||
/integrations/indexers-sqlite
|
||||
/integrations/indexers-mssql
|
||||
/integrations/sqlite.ini
|
||||
/integrations/mysql.ini
|
||||
/integrations/mysql8.ini
|
||||
/integrations/pgsql.ini
|
||||
/integrations/mssql.ini
|
||||
/node_modules
|
||||
/yarn.lock
|
||||
/public/js
|
||||
/public/serviceworker.js
|
||||
/public/css
|
||||
/public/fonts
|
||||
/public/img/webpack
|
||||
/web_src/fomantic/build
|
||||
/VERSION
|
||||
/.air
|
||||
/modules/indexer/issues/indexers
|
||||
|
||||
|
||||
# Snapcraft
|
||||
snap/.snapcraft/
|
||||
@@ -92,7 +76,3 @@ prime/
|
||||
*.snap
|
||||
*.snap-build
|
||||
*_source.tar.bz2
|
||||
.DS_Store
|
||||
|
||||
# Make evidence files
|
||||
/.make_evidence
|
||||
|
109
.golangci.yml
109
.golangci.yml
@@ -1,109 +0,0 @@
|
||||
linters:
|
||||
enable:
|
||||
- gosimple
|
||||
- deadcode
|
||||
- typecheck
|
||||
- govet
|
||||
- errcheck
|
||||
- staticcheck
|
||||
- unused
|
||||
- structcheck
|
||||
- varcheck
|
||||
- golint
|
||||
- dupl
|
||||
#- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
|
||||
- gofmt
|
||||
- misspell
|
||||
- gocritic
|
||||
enable-all: false
|
||||
disable-all: true
|
||||
fast: false
|
||||
|
||||
run:
|
||||
timeout: 3m
|
||||
|
||||
linters-settings:
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
- ifElseChain
|
||||
- singleCaseSwitch # Every time this occured in the code, there was no other way.
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
# Exclude some linters from running on tests files.
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- gocyclo
|
||||
- errcheck
|
||||
- dupl
|
||||
- gosec
|
||||
- unparam
|
||||
- staticcheck
|
||||
- path: models/migrations/v
|
||||
linters:
|
||||
- gocyclo
|
||||
- errcheck
|
||||
- dupl
|
||||
- gosec
|
||||
- linters:
|
||||
- dupl
|
||||
text: "webhook"
|
||||
- linters:
|
||||
- gocritic
|
||||
text: "`ID' should not be capitalized"
|
||||
- path: modules/templates/helper.go
|
||||
linters:
|
||||
- gocritic
|
||||
- linters:
|
||||
- unused
|
||||
- deadcode
|
||||
text: "swagger"
|
||||
- path: contrib/pr/checkout.go
|
||||
linters:
|
||||
- errcheck
|
||||
- path: models/issue.go
|
||||
linters:
|
||||
- errcheck
|
||||
- path: models/migrations/
|
||||
linters:
|
||||
- errcheck
|
||||
- path: modules/log/
|
||||
linters:
|
||||
- errcheck
|
||||
- path: routers/routes/routes.go
|
||||
linters:
|
||||
- dupl
|
||||
- path: routers/api/v1/repo/issue_subscription.go
|
||||
linters:
|
||||
- dupl
|
||||
- path: routers/repo/view.go
|
||||
linters:
|
||||
- dupl
|
||||
- path: models/migrations/
|
||||
linters:
|
||||
- unused
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: "argument x is overwritten before first use"
|
||||
- path: modules/httplib/httplib.go
|
||||
linters:
|
||||
- staticcheck
|
||||
# Enabling this would require refactoring the methods and how they are called.
|
||||
- path: models/issue_comment_list.go
|
||||
linters:
|
||||
- dupl
|
||||
- linters:
|
||||
- misspell
|
||||
text: '`Unknwon` is a misspelling of `Unknown`'
|
||||
- path: models/update.go
|
||||
linters:
|
||||
- unused
|
||||
- path: cmd/dump.go
|
||||
linters:
|
||||
- dupl
|
||||
- text: "commentFormatting: put a space between `//` and comment text"
|
||||
linters:
|
||||
- gocritic
|
||||
- text: "exitAfterDefer:"
|
||||
linters:
|
||||
- gocritic
|
5
.ignore
5
.ignore
@@ -1,5 +0,0 @@
|
||||
/vendor
|
||||
/public/vendor/plugins
|
||||
/modules/options/bindata.go
|
||||
/modules/public/bindata.go
|
||||
/modules/templates/bindata.go
|
15
.stylelintrc
15
.stylelintrc
@@ -1,15 +0,0 @@
|
||||
extends: stylelint-config-standard
|
||||
|
||||
rules:
|
||||
at-rule-empty-line-before: null
|
||||
block-closing-brace-empty-line-before: null
|
||||
color-hex-length: null
|
||||
comment-empty-line-before: null
|
||||
declaration-block-single-line-max-declarations: null
|
||||
declaration-empty-line-before: null
|
||||
indentation: 2
|
||||
no-descending-specificity: null
|
||||
number-leading-zero: never
|
||||
rule-empty-line-before: null
|
||||
selector-pseudo-element-colon-notation: null
|
||||
shorthand-property-no-redundant-values: true
|
2400
CHANGELOG.md
2400
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
153
CONTRIBUTING.md
153
CONTRIBUTING.md
@@ -11,9 +11,7 @@
|
||||
- [Translation](#translation)
|
||||
- [Code review](#code-review)
|
||||
- [Styleguide](#styleguide)
|
||||
- [Design guideline](#design-guideline)
|
||||
- [API v1](#api-v1)
|
||||
- [Developer Certificate of Origin (DCO)](#developer-certificate-of-origin-dco)
|
||||
- [Sign-off your work](#sign-off-your-work)
|
||||
- [Release Cycle](#release-cycle)
|
||||
- [Maintainers](#maintainers)
|
||||
- [Owners](#owners)
|
||||
@@ -67,30 +65,30 @@ high-level discussions.
|
||||
## Testing redux
|
||||
|
||||
Before submitting a pull request, run all the tests for the whole tree
|
||||
to make sure your changes don't cause regression elsewhere.
|
||||
to make sure your changes don't cause regression elsewhere.
|
||||
|
||||
Here's how to run the test suite:
|
||||
Here's how to run the test suite:
|
||||
|
||||
- code lint
|
||||
- Install the correct version of the drone-cli package. As of this
|
||||
writing, the correct drone-cli version is
|
||||
[0.8.6](https://0-8-0.docs.drone.io/cli-installation/).
|
||||
- Ensure you have enough free disk space. You will need at least
|
||||
15-20 Gb of free disk space to hold all of the containers drone
|
||||
creates (a default AWS or GCE disk size won't work -- see
|
||||
[#6243](https://github.com/go-gitea/gitea/issues/6243)).
|
||||
- Change into the base directory of your copy of the gitea repository,
|
||||
and run `drone exec --local --build-event pull_request`.
|
||||
|
||||
| | |
|
||||
| :-------------------- | :---------------------------------------------------------------- |
|
||||
|``make lint`` | lint everything (not suggest if you only change one type code) |
|
||||
|``make lint-frontend`` | lint frontend files |
|
||||
|``make lint-backend`` | lint backend files |
|
||||
|
||||
- run test code (Suggest run in linux)
|
||||
|
||||
| | |
|
||||
| :------------------------------------- | :----------------------------------------------- |
|
||||
|``make test[\#TestSpecificName]`` | run unit test |
|
||||
|``make test-sqlite[\#TestSpecificName]``| run [integration](integrations) test for sqlite |
|
||||
|[More detail message about integrations](integrations/README.md) |
|
||||
The drone version, command line, and disk requirements do change over
|
||||
time (see [#4053](https://github.com/go-gitea/gitea/issues/4053) and
|
||||
[#6243](https://github.com/go-gitea/gitea/issues/6243)); if you
|
||||
discover any issues, please feel free to send us a pull request to
|
||||
update these instructions.
|
||||
|
||||
## Vendoring
|
||||
|
||||
We keep a cached copy of dependencies within the `vendor/` directory,
|
||||
managing updates via [Modules](https://golang.org/cmd/go/#hdr-Module_maintenance).
|
||||
managing updates via [dep](https://github.com/golang/dep).
|
||||
|
||||
Pull requests should only include `vendor/` updates if they are part of
|
||||
the same change, be it a bugfix or a feature addition.
|
||||
@@ -99,7 +97,7 @@ The `vendor/` update needs to be justified as part of the PR description,
|
||||
and must be verified by the reviewers and/or merger to always reference
|
||||
an existing upstream commit.
|
||||
|
||||
You can find more information on how to get started with it on the [Modules Wiki](https://github.com/golang/go/wiki/Modules).
|
||||
You can find more information on how to get started with it on the [dep project website](https://golang.github.io/dep/docs/introduction.html).
|
||||
|
||||
## Translation
|
||||
|
||||
@@ -112,7 +110,13 @@ included in the next released version.
|
||||
|
||||
## Building Gitea
|
||||
|
||||
See the [hacking instructions](https://docs.gitea.io/en-us/hacking-on-gitea/).
|
||||
Generally, the go build tools are installed as-needed in the `Makefile`.
|
||||
An exception are the tools to build the CSS and images.
|
||||
|
||||
- To build CSS: Install [Node.js](https://nodejs.org/en/download/package-manager) at version 8.0 or above
|
||||
with `npm` and then run `npm install` and `make generate-stylesheets`.
|
||||
- To build Images: ImageMagick, inkscape and zopflipng binaries must be
|
||||
available in your `PATH` to run `make generate-images`.
|
||||
|
||||
## Code review
|
||||
|
||||
@@ -153,74 +157,22 @@ import (
|
||||
)
|
||||
```
|
||||
|
||||
## Design guideline
|
||||
## Sign-off your work
|
||||
|
||||
To maintain understandable code and avoid circular dependencies it is important to have a good structure of the code. The gitea code is divided into the following parts:
|
||||
|
||||
- **integration:** Integrations tests
|
||||
- **models:** Contains the data structures used by xorm to construct database tables. It also contains supporting functions to query and update the database. Dependecies to other code in Gitea should be avoided although some modules might be needed (for example for logging).
|
||||
- **models/fixtures:** Sample model data used in integration tests.
|
||||
- **models/migrations:** Handling of database migrations between versions. PRs that changes a database structure shall also have a migration step.
|
||||
- **modules:** Different modules to handle specific functionality in Gitea.
|
||||
- **public:** Frontend files (javascript, images, css, etc.)
|
||||
- **routers:** Handling of server requests. As it uses other Gitea packages to serve the request, other packages (models, modules or services) shall not depend on routers
|
||||
- **services:** Support functions for common routing operations. Uses models and modules to handle the request.
|
||||
- **templates:** Golang templates for generating the html output.
|
||||
- **vendor:** External code that Gitea depends on.
|
||||
|
||||
## API v1
|
||||
|
||||
The API is documented by [swagger](http://try.gitea.io/api/swagger) and is based on [GitHub API v3](https://developer.github.com/v3/).
|
||||
Thus, Gitea´s API should use the same endpoints and fields as GitHub´s API as far as possible, unless there are good reasons to deviate.
|
||||
If Gitea provides functionality that GitHub does not, a new endpoint can be created.
|
||||
If information is provided by Gitea that is not provided by the GitHub API, a new field can be used that doesn't collide with any GitHub fields.
|
||||
|
||||
Updating an existing API should not remove existing fields unless there is a really good reason to do so.
|
||||
The same applies to status responses. If you notice a problem, feel free to leave a comment in the code for future refactoring to APIv2 (which is currently not planned).
|
||||
|
||||
All expected results (errors, success, fail messages) should be documented
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L319-L327)).
|
||||
|
||||
All JSON input types must be defined as a struct in `models/structs/`
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/modules/structs/issue.go#L76-L91))
|
||||
and referenced in
|
||||
[routers/api/v1/swagger/options.go](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/swagger/options.go).
|
||||
They can then be used like the following:
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L318)).
|
||||
|
||||
All JSON responses must be defined as a struct in `models/structs/`
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/modules/structs/issue.go#L36-L68))
|
||||
and referenced in its category in `routers/api/v1/swagger/`
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/swagger/issue.go#L11-L16))
|
||||
They can be used like the following:
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L277-L279))
|
||||
|
||||
In general, HTTP methods are chosen as follows:
|
||||
* **GET** endpoints return requested object and status **OK (200)**
|
||||
* **DELETE** endpoints return status **No Content (204)**
|
||||
* **POST** endpoints return status **Created (201)**, used to **create** new objects (e.g. a User)
|
||||
* **PUT** endpoints return status **No Content (204)**, used to **add/assign** existing Obejcts (e.g. User) to something (e.g. Org-Team)
|
||||
* **PATCH** endpoints return changed object and status **OK (200)**, used to **edit/change** an existing object
|
||||
|
||||
|
||||
An endpoint which changes/edits an object expects all fields to be optional (except ones to identify the object, which are required).
|
||||
|
||||
|
||||
## Developer Certificate of Origin (DCO)
|
||||
|
||||
We consider the act of contributing to the code by submitting a Pull
|
||||
Request as the "Sign off" or agreement to the certifications and terms
|
||||
of the [DCO](DCO) and [MIT license](LICENSE). No further action is required.
|
||||
Additionally you could add a line at the end of your commit message.
|
||||
The sign-off is a simple line at the end of the explanation for the
|
||||
patch. Your signature certifies that you wrote the patch or otherwise
|
||||
have the right to pass it on as an open-source patch. The rules are
|
||||
pretty simple: If you can certify [DCO](DCO), then you just add a line
|
||||
to every git commit message:
|
||||
|
||||
```
|
||||
Signed-off-by: Joe Smith <joe.smith@email.com>
|
||||
```
|
||||
|
||||
If you set your `user.name` and `user.email` git configs, you can add the
|
||||
line to the end of your commit automatically with `git commit -s`.
|
||||
|
||||
We assume in good faith that the information you provide is legally binding.
|
||||
Please use your real name; we really dislike pseudonyms or anonymous
|
||||
contributions. We are in the open-source world without secrets. If you
|
||||
set your `user.name` and `user.email` git configs, you can sign-off your
|
||||
commit automatically with `git commit -s`.
|
||||
|
||||
## Release Cycle
|
||||
|
||||
@@ -262,7 +214,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
|
||||
him or her from the maintainers team to the advisors team.
|
||||
For security reasons, Maintainers should use 2FA for their accounts and
|
||||
if possible provide gpg signed commits.
|
||||
if possible provide gpg signed commits.
|
||||
https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/
|
||||
https://help.github.com/articles/signing-commits-with-gpg/
|
||||
|
||||
@@ -293,26 +245,21 @@ and lead the development of Gitea.
|
||||
To honor the past owners, here's the history of the owners and the time
|
||||
they served:
|
||||
|
||||
* 2020-01-01 ~ 2020-12-31 - https://github.com/go-gitea/gitea/issues/9230
|
||||
* [Lunny Xiao](https://gitea.com/lunny) <xiaolunwen@gmail.com>
|
||||
* [Lauris Bukšis-Haberkorns](https://gitea.com/lafriks) <lauris@nix.lv>
|
||||
* [Matti Ranta](https://gitea.com/techknowlogick) <techknowlogick@gitea.io>
|
||||
|
||||
* 2019-01-01 ~ 2019-12-31 - https://github.com/go-gitea/gitea/issues/5572
|
||||
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
||||
* [Lauris Bukšis-Haberkorns](https://github.com/lafriks) <lauris@nix.lv>
|
||||
* [Matti Ranta](https://github.com/techknowlogick) <techknowlogick@gitea.io>
|
||||
|
||||
* 2018-01-01 ~ 2018-12-31 - https://github.com/go-gitea/gitea/issues/3255
|
||||
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
||||
* [Lauris Bukšis-Haberkorns](https://github.com/lafriks) <lauris@nix.lv>
|
||||
* [Kim Carlbäcker](https://github.com/bkcsoft) <kim.carlbacker@gmail.com>
|
||||
|
||||
* 2016-11-04 ~ 2017-12-31
|
||||
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
||||
* [Thomas Boerger](https://github.com/tboerger) <thomas@webhippie.de>
|
||||
* [Kim Carlbäcker](https://github.com/bkcsoft) <kim.carlbacker@gmail.com>
|
||||
|
||||
* 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) <matti@mdranta.net>
|
||||
|
||||
## Versions
|
||||
|
||||
Gitea has the `master` branch as a tip branch and has version branches
|
||||
@@ -334,17 +281,17 @@ be reviewed by two maintainers and must pass the automatic tests.
|
||||
* Create `-dev` tag as `git tag -s -F release.notes v$vmaj.$vmin.0-dev` and push the tag as `git push origin v$vmaj.$vmin.0-dev`.
|
||||
* When CI has finished building tag then you have to create a new branch named `release/v$vmaj.$vmin`
|
||||
* If it is bugfix version create PR for changelog on branch `release/v$vmaj.$vmin` and wait till it is reviewed and merged.
|
||||
* Add a tag as `git tag -s -F release.notes v$vmaj.$vmin.$`, release.notes file could be a temporary file to only include the changelog this version which you added to `CHANGELOG.md`.
|
||||
* Add a tag as `git tag -s -F release.notes v$vmaj.$vmin.$`, release.notes file could be a temporary file to only include the changelog this version which you added to `CHANGELOG.md`.
|
||||
* And then push the tag as `git push origin v$vmaj.$vmin.$`. Drone CI will automatically created a release and upload all the compiled binary. (But currently it didn't add the release notes automatically. Maybe we should fix that.)
|
||||
* If needed send PR for changelog on branch `master`.
|
||||
* Send PR to [blog repository](https://gitea.com/gitea/blog) announcing the release.
|
||||
* Send PR to [blog repository](https://github.com/go-gitea/blog) announcing the release.
|
||||
|
||||
## Copyright
|
||||
|
||||
Code that you contribute should use the standard copyright header:
|
||||
|
||||
```
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
```
|
||||
|
18
Dockerfile
18
Dockerfile
@@ -1,18 +1,14 @@
|
||||
|
||||
###################################
|
||||
#Build stage
|
||||
FROM golang:1.15-alpine3.12 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY ${GOPROXY:-direct}
|
||||
FROM golang:1.12-alpine3.9 AS build-env
|
||||
|
||||
ARG GITEA_VERSION
|
||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
||||
ENV TAGS "bindata timetzdata $TAGS"
|
||||
ARG CGO_EXTRA_CFLAGS
|
||||
ENV TAGS "bindata $TAGS"
|
||||
|
||||
#Build deps
|
||||
RUN apk --no-cache add build-base git nodejs npm
|
||||
RUN apk --no-cache add build-base git
|
||||
|
||||
#Setup repo
|
||||
COPY . ${GOPATH}/src/code.gitea.io/gitea
|
||||
@@ -20,9 +16,9 @@ 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
|
||||
&& make clean generate build
|
||||
|
||||
FROM alpine:3.12
|
||||
FROM alpine:3.9
|
||||
LABEL maintainer="maintainers@gitea.io"
|
||||
|
||||
EXPOSE 22 3000
|
||||
@@ -38,7 +34,7 @@ RUN apk --no-cache add \
|
||||
s6 \
|
||||
sqlite \
|
||||
su-exec \
|
||||
gnupg
|
||||
tzdata
|
||||
|
||||
RUN addgroup \
|
||||
-S -g 1000 \
|
||||
@@ -60,6 +56,6 @@ VOLUME ["/data"]
|
||||
ENTRYPOINT ["/usr/bin/entrypoint"]
|
||||
CMD ["/bin/s6-svscan", "/etc/s6"]
|
||||
|
||||
COPY docker/root /
|
||||
COPY docker /
|
||||
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
||||
RUN ln -s /app/gitea/gitea /usr/local/bin/gitea
|
||||
|
1341
Gopkg.lock
generated
Normal file
1341
Gopkg.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
119
Gopkg.toml
Normal file
119
Gopkg.toml
Normal file
@@ -0,0 +1,119 @@
|
||||
|
||||
ignored = ["google.golang.org/appengine*"]
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
non-go = true
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "code.gitea.io/git"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "code.gitea.io/sdk"
|
||||
|
||||
[[constraint]]
|
||||
revision = "05d86ea8f6e30456949f612cf68cf4a27ce8c9c5"
|
||||
name = "github.com/blevesearch/bleve"
|
||||
|
||||
[[constraint]]
|
||||
revision = "12dd70caea0268ac0d6c2707d0611ef601e7c64e"
|
||||
name = "golang.org/x/crypto"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
|
||||
[[constraint]]
|
||||
revision = "2bf8f2a19ec09c670e931282edfe6567f6be21c9"
|
||||
name = "golang.org/x/text"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/go-xorm/xorm"
|
||||
revision = "a6300f2a45e05a8f75f00a1d6188049fe7851915"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/go-xorm/builder"
|
||||
version = "0.3.3"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/go-sql-driver/mysql"
|
||||
revision = "c45f530f8e7fe40f4687eaa50d0c8c5f1b66f9e0"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/mattn/go-sqlite3"
|
||||
revision = "c7c4067b79cc51e6dfdcef5c702e74b1e0fa7c75"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/gorilla/mux"
|
||||
revision = "757bef944d0f21880861c2dd9c871ca543023cba"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gorilla/context"
|
||||
version = "1.1.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/lafriks/xormstore"
|
||||
version = "1.0.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/lunny/dingtalk_webhook"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/markbates/goth"
|
||||
version = "1.47.2"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/mcuadros/go-version"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/russross/blackfriday"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/tstranex/u2f"
|
||||
|
||||
[[constraint]]
|
||||
name = "gopkg.in/editorconfig/editorconfig-core-go.v1"
|
||||
version = "1.2.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "v2"
|
||||
name = "gopkg.in/gomail.v2"
|
||||
|
||||
[[constraint]]
|
||||
name = "gopkg.in/ini.v1"
|
||||
version = "1.31.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "gopkg.in/ldap.v3"
|
||||
version = "3.0.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "gopkg.in/macaron.v1"
|
||||
version = "1.2.4"
|
||||
|
||||
[[constraint]]
|
||||
name = "gopkg.in/testfixtures.v2"
|
||||
version = "2.0.0"
|
||||
|
||||
[[override]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/oauth2"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/prometheus/client_golang"
|
||||
version = "0.9.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mvdan/xurls"
|
||||
version = "2.0.0"
|
11
MAINTAINERS
11
MAINTAINERS
@@ -19,7 +19,7 @@ Lauris Bukšis-Haberkorns <lauris@nix.lv> (@lafriks)
|
||||
Jonas Östanbäck <jonas.ostanback@gmail.com> (@cez81)
|
||||
David Schneiderbauer <dschneiderbauer@gmail.com> (@daviian)
|
||||
Peter Žeby <morlinest@gmail.com> (@morlinest)
|
||||
Matti Ranta <techknowlogick@gitea.io> (@techknowlogick)
|
||||
Matti Ranta <matti@mdranta.net> (@techknowlogick)
|
||||
Jonas Franz <info@jonasfranz.software> (@jonasfranz)
|
||||
Alexey Terentyev <axifnx@gmail.com> (@axifive)
|
||||
Lanre Adelowo <yo@lanre.wtf> (@adelowo)
|
||||
@@ -28,12 +28,3 @@ He-Long Zhang <outman99@hotmail.com> (@BetaCat0)
|
||||
Andrew Thornton <art27@cantab.net> (@zeripath)
|
||||
John Olheiser <john.olheiser@gmail.com> (@jolheiser)
|
||||
Richard Mahn <rich.mahn@unfoldingword.org> (@richmahn)
|
||||
Mrsdizzie <info@mrsdizzie.com> (@mrsdizzie)
|
||||
silverwind <me@silverwind.io> (@silverwind)
|
||||
Gary Kim <gary@garykim.dev> (@gary-kim)
|
||||
Guillermo Prandi <gitea.maint@mailfilter.com.ar> (@guillep2k)
|
||||
Mura Li <typeless@ctli.io> (@typeless)
|
||||
6543 <6543@obermui.de> (@6543)
|
||||
jaqra <jaqra@hotmail.com> (@jaqra)
|
||||
David Svantesson <davidsvantesson@gmail.com> (@davidsvantesson)
|
||||
CirnoT <gitea.m@i32.pl> (@CirnoT)
|
||||
|
672
Makefile
672
Makefile
@@ -1,132 +1,53 @@
|
||||
|
||||
ifeq ($(USE_REPO_TEST_DIR),1)
|
||||
|
||||
# This rule replaces the whole Makefile when we're trying to use /tmp repository temporary files
|
||||
location = $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
|
||||
self := $(location)
|
||||
|
||||
%:
|
||||
@tmpdir=`mktemp --tmpdir -d` ; \
|
||||
echo Using temporary directory $$tmpdir for test repositories ; \
|
||||
USE_REPO_TEST_DIR= $(MAKE) -f $(self) --no-print-directory REPO_TEST_DIR=$$tmpdir/ $@ ; \
|
||||
STATUS=$$? ; rm -r "$$tmpdir" ; exit $$STATUS
|
||||
|
||||
else
|
||||
|
||||
# This is the "normal" part of the Makefile
|
||||
|
||||
DIST := dist
|
||||
DIST_DIRS := $(DIST)/binaries $(DIST)/release
|
||||
IMPORT := code.gitea.io/gitea
|
||||
export GO111MODULE=on
|
||||
|
||||
GO ?= go
|
||||
SED_INPLACE := sed -i
|
||||
SHASUM ?= shasum -a 256
|
||||
HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
|
||||
COMMA := ,
|
||||
|
||||
XGO_VERSION := go-1.15.x
|
||||
MIN_GO_VERSION := 001013000
|
||||
MIN_NODE_VERSION := 010013000
|
||||
|
||||
DOCKER_IMAGE ?= gitea/gitea
|
||||
DOCKER_TAG ?= latest
|
||||
DOCKER_REF := $(DOCKER_IMAGE):$(DOCKER_TAG)
|
||||
|
||||
ifeq ($(HAS_GO), GO)
|
||||
GOPATH ?= $(shell $(GO) env GOPATH)
|
||||
export PATH := $(GOPATH)/bin:$(PATH)
|
||||
|
||||
CGO_EXTRA_CFLAGS := -DSQLITE_MAX_VARIABLE_NUMBER=32766
|
||||
CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS)
|
||||
endif
|
||||
export PATH := $($(GO) env GOPATH)/bin:$(PATH)
|
||||
|
||||
ifeq ($(OS), Windows_NT)
|
||||
GOFLAGS := -v -buildmode=exe
|
||||
EXECUTABLE ?= gitea.exe
|
||||
EXECUTABLE := gitea.exe
|
||||
else
|
||||
GOFLAGS := -v
|
||||
EXECUTABLE ?= gitea
|
||||
endif
|
||||
|
||||
ifeq ($(shell sed --version 2>/dev/null | grep -q GNU && echo gnu),gnu)
|
||||
SED_INPLACE := sed -i
|
||||
else
|
||||
SED_INPLACE := sed -i ''
|
||||
EXECUTABLE := gitea
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
SED_INPLACE := sed -i ''
|
||||
endif
|
||||
endif
|
||||
|
||||
BINDATA := modules/{options,public,templates}/bindata.go
|
||||
GOFILES := $(shell find . -name "*.go" -type f ! -path "./vendor/*" ! -path "*/bindata.go")
|
||||
GOFMT ?= gofmt -s
|
||||
|
||||
GOFLAGS := -i -v
|
||||
EXTRA_GOFLAGS ?=
|
||||
|
||||
MAKE_VERSION := $(shell $(MAKE) -v | head -n 1)
|
||||
MAKE_EVIDENCE_DIR := .make_evidence
|
||||
|
||||
ifneq ($(RACE_ENABLED),)
|
||||
GOTESTFLAGS ?= -race
|
||||
endif
|
||||
|
||||
STORED_VERSION_FILE := VERSION
|
||||
|
||||
ifneq ($(DRONE_TAG),)
|
||||
VERSION ?= $(subst v,,$(DRONE_TAG))
|
||||
GITEA_VERSION ?= $(VERSION)
|
||||
GITEA_VERSION := $(VERSION)
|
||||
else
|
||||
ifneq ($(DRONE_BRANCH),)
|
||||
VERSION ?= $(subst release/v,,$(DRONE_BRANCH))
|
||||
else
|
||||
VERSION ?= master
|
||||
endif
|
||||
|
||||
STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null)
|
||||
ifneq ($(STORED_VERSION),)
|
||||
GITEA_VERSION ?= $(STORED_VERSION)
|
||||
else
|
||||
GITEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
|
||||
endif
|
||||
GITEA_VERSION := $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
|
||||
endif
|
||||
|
||||
LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
|
||||
LDFLAGS := -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
|
||||
|
||||
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/)))
|
||||
|
||||
FOMANTIC_CONFIGS := semantic.json web_src/fomantic/theme.config.less web_src/fomantic/_site/globals/site.variables
|
||||
FOMANTIC_DEST := web_src/fomantic/build/semantic.js web_src/fomantic/build/semantic.css
|
||||
FOMANTIC_DEST_DIR := web_src/fomantic/build
|
||||
|
||||
WEBPACK_SOURCES := $(shell find web_src/js web_src/less -type f) $(FOMANTIC_DEST)
|
||||
WEBPACK_CONFIGS := webpack.config.js
|
||||
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
|
||||
|
||||
BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
|
||||
BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))
|
||||
|
||||
SVG_DEST_DIR := public/img/svg
|
||||
|
||||
AIR_TMP_DIR := .air
|
||||
PACKAGES ?= $(filter-out code.gitea.io/gitea/integrations/migration-test,$(filter-out code.gitea.io/gitea/integrations,$(shell $(GO) list ./... | grep -v /vendor/)))
|
||||
SOURCES ?= $(shell find . -name "*.go" -type f)
|
||||
|
||||
TAGS ?=
|
||||
TAGS_SPLIT := $(subst $(COMMA), ,$(TAGS))
|
||||
TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
|
||||
|
||||
GO_DIRS := cmd integrations models modules routers build services vendor
|
||||
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)
|
||||
TMPDIR := $(shell mktemp -d 2>/dev/null || mktemp -d -t 'gitea-temp')
|
||||
|
||||
ifeq ($(filter $(TAGS_SPLIT),bindata),bindata)
|
||||
GO_SOURCES += $(BINDATA_DEST)
|
||||
endif
|
||||
|
||||
GO_SOURCES_OWN := $(filter-out vendor/% %/bindata.go, $(GO_SOURCES))
|
||||
|
||||
#To update swagger use: GO111MODULE=on go get -u github.com/go-swagger/go-swagger/cmd/swagger
|
||||
SWAGGER := $(GO) run -mod=vendor github.com/go-swagger/go-swagger/cmd/swagger
|
||||
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
|
||||
SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl}}/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_SPEC_S_TMPL := s|"basePath":\s*"/api/v1"|"basePath": "{{AppSubUrl}}/api/v1"|g
|
||||
SWAGGER_SPEC_S_JSON := s|"basePath":\s*"{{AppSubUrl}}/api/v1"|"basePath": "/api/v1"|g
|
||||
|
||||
TEST_MYSQL_HOST ?= mysql:3306
|
||||
TEST_MYSQL_DBNAME ?= testgitea
|
||||
@@ -140,82 +61,29 @@ TEST_PGSQL_HOST ?= pgsql:5432
|
||||
TEST_PGSQL_DBNAME ?= testgitea
|
||||
TEST_PGSQL_USERNAME ?= postgres
|
||||
TEST_PGSQL_PASSWORD ?= postgres
|
||||
TEST_PGSQL_SCHEMA ?= gtestschema
|
||||
TEST_MSSQL_HOST ?= mssql:1433
|
||||
TEST_MSSQL_DBNAME ?= gitea
|
||||
TEST_MSSQL_USERNAME ?= sa
|
||||
TEST_MSSQL_PASSWORD ?= MwantsaSecurePassword1
|
||||
|
||||
ifeq ($(OS), Windows_NT)
|
||||
EXECUTABLE := gitea.exe
|
||||
else
|
||||
EXECUTABLE := gitea
|
||||
endif
|
||||
|
||||
# $(call strip-suffix,filename)
|
||||
strip-suffix = $(firstword $(subst ., ,$(1)))
|
||||
|
||||
.PHONY: all
|
||||
all: build
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo "Make Routines:"
|
||||
@echo " - \"\" equivalent to \"build\""
|
||||
@echo " - build build everything"
|
||||
@echo " - frontend build frontend files"
|
||||
@echo " - backend build backend files"
|
||||
@echo " - watch watch everything and continuously rebuild"
|
||||
@echo " - watch-frontend watch frontend files and continuously rebuild"
|
||||
@echo " - watch-backend watch backend files and continuously rebuild"
|
||||
@echo " - clean delete backend and integration files"
|
||||
@echo " - clean-all delete backend, frontend and integration files"
|
||||
@echo " - lint lint everything"
|
||||
@echo " - lint-frontend lint frontend files"
|
||||
@echo " - lint-backend lint backend files"
|
||||
@echo " - checks run various consistency checks"
|
||||
@echo " - checks-frontend check frontend files"
|
||||
@echo " - checks-backend check backend files"
|
||||
@echo " - webpack build webpack files"
|
||||
@echo " - svg build svg files"
|
||||
@echo " - fomantic build fomantic files"
|
||||
@echo " - generate run \"go generate\""
|
||||
@echo " - fmt format the Go code"
|
||||
@echo " - generate-license update license files"
|
||||
@echo " - generate-gitignore update gitignore files"
|
||||
@echo " - generate-swagger generate the swagger spec from code comments"
|
||||
@echo " - swagger-validate check if the swagger spec is valid"
|
||||
@echo " - golangci-lint run golangci-lint linter"
|
||||
@echo " - revive run revive linter"
|
||||
@echo " - misspell check for misspellings"
|
||||
@echo " - vet examines Go source code and reports suspicious constructs"
|
||||
@echo " - test[\#TestSpecificName] run unit test"
|
||||
@echo " - test-sqlite[\#TestSpecificName] run integration test for sqlite"
|
||||
@echo " - pr#<index> build and start gitea from a PR with integration test data loaded"
|
||||
|
||||
.PHONY: go-check
|
||||
go-check:
|
||||
$(eval GO_VERSION := $(shell printf "%03d%03d%03d" $(shell go version | grep -Eo '[0-9]+\.[0-9.]+' | tr '.' ' ');))
|
||||
@if [ "$(GO_VERSION)" -lt "$(MIN_GO_VERSION)" ]; then \
|
||||
echo "Gitea requires Go 1.13 or greater to build. You can get it at https://golang.org/dl/"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: git-check
|
||||
git-check:
|
||||
@if git lfs >/dev/null 2>&1 ; then : ; else \
|
||||
echo "Gitea requires git with lfs support to run tests." ; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: node-check
|
||||
node-check:
|
||||
$(eval NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v | cut -c2- | tr '.' ' ');))
|
||||
$(eval NPM_MISSING := $(shell hash npm > /dev/null 2>&1 || echo 1))
|
||||
@if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" -o "$(NPM_MISSING)" = "1" ]; then \
|
||||
echo "Gitea requires Node.js 10 or greater and npm to build. You can get it at https://nodejs.org/en/download/"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: clean-all
|
||||
clean-all: clean
|
||||
rm -rf $(WEBPACK_DEST_ENTRIES) $(FOMANTIC_DEST_DIR)
|
||||
include docker/Makefile
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
$(GO) clean -i ./...
|
||||
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) $(BINDATA_HASH) \
|
||||
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA) \
|
||||
integrations*.test \
|
||||
integrations/gitea-integration-pgsql/ integrations/gitea-integration-mysql/ integrations/gitea-integration-mysql8/ integrations/gitea-integration-sqlite/ \
|
||||
integrations/gitea-integration-mssql/ integrations/indexers-mysql/ integrations/indexers-mysql8/ integrations/indexers-pgsql integrations/indexers-sqlite \
|
||||
@@ -223,30 +91,26 @@ clean:
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
$(GOFMT) -w $(GO_SOURCES_OWN)
|
||||
$(GOFMT) -w $(GOFILES)
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
# Default vet
|
||||
$(GO) vet $(GO_PACKAGES)
|
||||
# Custom vet
|
||||
$(GO) build -mod=vendor code.gitea.io/gitea-vet
|
||||
$(GO) vet -vettool=gitea-vet $(GO_PACKAGES)
|
||||
$(GO) vet $(PACKAGES)
|
||||
|
||||
.PHONY: $(TAGS_EVIDENCE)
|
||||
$(TAGS_EVIDENCE):
|
||||
@mkdir -p $(MAKE_EVIDENCE_DIR)
|
||||
@echo "$(TAGS)" > $(TAGS_EVIDENCE)
|
||||
|
||||
ifneq "$(TAGS)" "$(shell cat $(TAGS_EVIDENCE) 2>/dev/null)"
|
||||
TAGS_PREREQ := $(TAGS_EVIDENCE)
|
||||
endif
|
||||
.PHONY: generate
|
||||
generate:
|
||||
@hash go-bindata > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) get -u github.com/jteeuwen/go-bindata/go-bindata; \
|
||||
fi
|
||||
$(GO) generate $(PACKAGES)
|
||||
|
||||
.PHONY: generate-swagger
|
||||
generate-swagger:
|
||||
$(SWAGGER) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
|
||||
@hash swagger > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) get -u github.com/go-swagger/go-swagger/cmd/swagger; \
|
||||
fi
|
||||
swagger generate spec -o './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
|
||||
|
||||
.PHONY: swagger-check
|
||||
swagger-check: generate-swagger
|
||||
@@ -259,112 +123,72 @@ swagger-check: generate-swagger
|
||||
|
||||
.PHONY: swagger-validate
|
||||
swagger-validate:
|
||||
@hash swagger > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) get -u github.com/go-swagger/go-swagger/cmd/swagger; \
|
||||
fi
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)'
|
||||
$(SWAGGER) validate './$(SWAGGER_SPEC)'
|
||||
swagger validate './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||
|
||||
.PHONY: errcheck
|
||||
errcheck:
|
||||
@hash errcheck > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u github.com/kisielk/errcheck; \
|
||||
$(GO) get -u github.com/kisielk/errcheck; \
|
||||
fi
|
||||
errcheck $(GO_PACKAGES)
|
||||
errcheck $(PACKAGES)
|
||||
|
||||
.PHONY: revive
|
||||
revive:
|
||||
GO111MODULE=on $(GO) run -mod=vendor build/lint.go -config .revive.toml -exclude=./vendor/... ./... || exit 1
|
||||
.PHONY: lint
|
||||
lint:
|
||||
@hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) get -u github.com/mgechev/revive; \
|
||||
fi
|
||||
revive -config .revive.toml -exclude=./vendor/... ./... || exit 1
|
||||
|
||||
.PHONY: misspell-check
|
||||
misspell-check:
|
||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
||||
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
||||
fi
|
||||
misspell -error -i unknwon,destory $(GO_SOURCES_OWN)
|
||||
misspell -error -i unknwon,destory $(GOFILES)
|
||||
|
||||
.PHONY: misspell
|
||||
misspell:
|
||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
||||
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
||||
fi
|
||||
misspell -w -i unknwon $(GO_SOURCES_OWN)
|
||||
misspell -w -i unknwon $(GOFILES)
|
||||
|
||||
.PHONY: fmt-check
|
||||
fmt-check:
|
||||
# get all go files and run go fmt on them
|
||||
@diff=$$($(GOFMT) -d $(GO_SOURCES_OWN)); \
|
||||
@diff=$$($(GOFMT) -d $(GOFILES)); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make fmt' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi;
|
||||
|
||||
.PHONY: checks
|
||||
checks: checks-frontend checks-backend
|
||||
|
||||
.PHONY: checks-frontend
|
||||
checks-frontend: svg-check
|
||||
|
||||
.PHONY: checks-backend
|
||||
checks-backend: misspell-check test-vendor swagger-check swagger-validate
|
||||
|
||||
.PHONY: lint
|
||||
lint: lint-frontend lint-backend
|
||||
|
||||
.PHONY: lint-frontend
|
||||
lint-frontend: node_modules
|
||||
npx eslint web_src/js build webpack.config.js
|
||||
npx stylelint web_src/less
|
||||
|
||||
.PHONY: lint-backend
|
||||
lint-backend: golangci-lint revive vet
|
||||
|
||||
.PHONY: watch
|
||||
watch:
|
||||
bash tools/watch.sh
|
||||
|
||||
.PHONY: watch-frontend
|
||||
watch-frontend: node-check $(FOMANTIC_DEST) node_modules
|
||||
rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||
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 \
|
||||
GO111MODULE=off $(GO) get -u github.com/cosmtrek/air; \
|
||||
fi
|
||||
air -c .air.conf
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -tags='sqlite sqlite_unlock_notify' $(GO_PACKAGES)
|
||||
|
||||
.PHONY: test-check
|
||||
test-check:
|
||||
@echo "Checking if tests have changed the source tree...";
|
||||
@diff=$$(git status -s); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "make test has changed files in the source tree:"; \
|
||||
echo "$${diff}"; \
|
||||
echo "You should change the tests to create these files in a temporary directory."; \
|
||||
echo "Do not simply add these files to .gitignore"; \
|
||||
exit 1; \
|
||||
fi;
|
||||
|
||||
.PHONY: test\#%
|
||||
test\#%:
|
||||
$(GO) test -mod=vendor -tags='sqlite sqlite_unlock_notify' -run $(subst .,/,$*) $(GO_PACKAGES)
|
||||
$(GO) test -tags='sqlite sqlite_unlock_notify' $(PACKAGES)
|
||||
|
||||
.PHONY: coverage
|
||||
coverage:
|
||||
GO111MODULE=on $(GO) run -mod=vendor build/gocovmerge.go integration.coverage.out $(shell find . -type f -name "coverage.out") > coverage.all
|
||||
@hash gocovmerge > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) get -u github.com/wadey/gocovmerge; \
|
||||
fi
|
||||
gocovmerge integration.coverage.out $(shell find . -type f -name "coverage.out") > coverage.all;\
|
||||
|
||||
.PHONY: unit-test-coverage
|
||||
unit-test-coverage:
|
||||
$(GO) test $(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 -tags='sqlite sqlite_unlock_notify' -cover -coverprofile coverage.out $(PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
|
||||
|
||||
.PHONY: vendor
|
||||
vendor:
|
||||
$(GO) mod tidy && $(GO) mod vendor
|
||||
@hash dep > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) get -u github.com/golang/dep/cmd/dep; \
|
||||
fi
|
||||
dep ensure -vendor-only
|
||||
|
||||
.PHONY: test-vendor
|
||||
test-vendor: vendor
|
||||
@@ -374,298 +198,209 @@ test-vendor: vendor
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi;
|
||||
|
||||
generate-ini-sqlite:
|
||||
sed -e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||
integrations/sqlite.ini.tmpl > integrations/sqlite.ini
|
||||
#TODO add dep status -missing when implemented
|
||||
|
||||
.PHONY: test-sqlite
|
||||
test-sqlite: integrations.sqlite.test generate-ini-sqlite
|
||||
test-sqlite: integrations.sqlite.test
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test
|
||||
|
||||
.PHONY: test-sqlite\#%
|
||||
test-sqlite\#%: integrations.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-sqlite-migration
|
||||
test-sqlite-migration: migrations.sqlite.test generate-ini-sqlite
|
||||
test-sqlite-migration: migrations.sqlite.test
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./migrations.sqlite.test
|
||||
|
||||
generate-ini-mysql:
|
||||
generate-ini:
|
||||
sed -e 's|{{TEST_MYSQL_HOST}}|${TEST_MYSQL_HOST}|g' \
|
||||
-e 's|{{TEST_MYSQL_DBNAME}}|${TEST_MYSQL_DBNAME}|g' \
|
||||
-e 's|{{TEST_MYSQL_USERNAME}}|${TEST_MYSQL_USERNAME}|g' \
|
||||
-e 's|{{TEST_MYSQL_PASSWORD}}|${TEST_MYSQL_PASSWORD}|g' \
|
||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||
integrations/mysql.ini.tmpl > integrations/mysql.ini
|
||||
|
||||
.PHONY: test-mysql
|
||||
test-mysql: integrations.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test
|
||||
|
||||
.PHONY: test-mysql\#%
|
||||
test-mysql\#%: integrations.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-mysql-migration
|
||||
test-mysql-migration: migrations.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./migrations.mysql.test
|
||||
|
||||
generate-ini-mysql8:
|
||||
sed -e 's|{{TEST_MYSQL8_HOST}}|${TEST_MYSQL8_HOST}|g' \
|
||||
-e 's|{{TEST_MYSQL8_DBNAME}}|${TEST_MYSQL8_DBNAME}|g' \
|
||||
-e 's|{{TEST_MYSQL8_USERNAME}}|${TEST_MYSQL8_USERNAME}|g' \
|
||||
-e 's|{{TEST_MYSQL8_PASSWORD}}|${TEST_MYSQL8_PASSWORD}|g' \
|
||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||
integrations/mysql8.ini.tmpl > integrations/mysql8.ini
|
||||
|
||||
.PHONY: test-mysql8
|
||||
test-mysql8: integrations.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql8.ini ./integrations.mysql8.test
|
||||
|
||||
.PHONY: test-mysql8\#%
|
||||
test-mysql8\#%: integrations.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql8.ini ./integrations.mysql8.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-mysql8-migration
|
||||
test-mysql8-migration: migrations.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql8.ini ./migrations.mysql8.test
|
||||
|
||||
generate-ini-pgsql:
|
||||
sed -e 's|{{TEST_PGSQL_HOST}}|${TEST_PGSQL_HOST}|g' \
|
||||
-e 's|{{TEST_PGSQL_DBNAME}}|${TEST_PGSQL_DBNAME}|g' \
|
||||
-e 's|{{TEST_PGSQL_USERNAME}}|${TEST_PGSQL_USERNAME}|g' \
|
||||
-e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \
|
||||
-e 's|{{TEST_PGSQL_SCHEMA}}|${TEST_PGSQL_SCHEMA}|g' \
|
||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||
integrations/pgsql.ini.tmpl > integrations/pgsql.ini
|
||||
|
||||
.PHONY: test-pgsql
|
||||
test-pgsql: integrations.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test
|
||||
|
||||
.PHONY: test-pgsql\#%
|
||||
test-pgsql\#%: integrations.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-pgsql-migration
|
||||
test-pgsql-migration: migrations.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./migrations.pgsql.test
|
||||
|
||||
generate-ini-mssql:
|
||||
sed -e 's|{{TEST_MSSQL_HOST}}|${TEST_MSSQL_HOST}|g' \
|
||||
-e 's|{{TEST_MSSQL_DBNAME}}|${TEST_MSSQL_DBNAME}|g' \
|
||||
-e 's|{{TEST_MSSQL_USERNAME}}|${TEST_MSSQL_USERNAME}|g' \
|
||||
-e 's|{{TEST_MSSQL_PASSWORD}}|${TEST_MSSQL_PASSWORD}|g' \
|
||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||
integrations/mssql.ini.tmpl > integrations/mssql.ini
|
||||
|
||||
.PHONY: test-mssql
|
||||
test-mssql: integrations.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test
|
||||
.PHONY: test-mysql
|
||||
test-mysql: integrations.test generate-ini
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.test
|
||||
|
||||
.PHONY: test-mssql\#%
|
||||
test-mssql\#%: integrations.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test -test.run $(subst .,/,$*)
|
||||
.PHONY: test-mysql-migration
|
||||
test-mysql-migration: migrations.test generate-ini
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./migrations.test
|
||||
|
||||
.PHONY: test-mysql8
|
||||
test-mysql8: integrations.test generate-ini
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql8.ini ./integrations.test
|
||||
|
||||
.PHONY: test-mysql8-migration
|
||||
test-mysql8-migration: migrations.test generate-ini
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql8.ini ./migrations.test
|
||||
|
||||
.PHONY: test-pgsql
|
||||
test-pgsql: integrations.test generate-ini
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./integrations.test
|
||||
|
||||
.PHONY: test-pgsql-migration
|
||||
test-pgsql-migration: migrations.test generate-ini
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./migrations.test
|
||||
|
||||
.PHONY: test-mssql
|
||||
test-mssql: integrations.test generate-ini
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mssql.ini ./integrations.test
|
||||
|
||||
.PHONY: test-mssql-migration
|
||||
test-mssql-migration: migrations.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mssql.ini ./migrations.mssql.test -test.failfast
|
||||
test-mssql-migration: migrations.test generate-ini
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mssql.ini ./migrations.test
|
||||
|
||||
|
||||
.PHONY: bench-sqlite
|
||||
bench-sqlite: integrations.sqlite.test generate-ini-sqlite
|
||||
bench-sqlite: integrations.sqlite.test
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
.PHONY: bench-mysql
|
||||
bench-mysql: integrations.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
bench-mysql: integrations.test generate-ini
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
.PHONY: bench-mssql
|
||||
bench-mssql: integrations.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
bench-mssql: integrations.test generate-ini
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mssql.ini ./integrations.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
.PHONY: bench-pgsql
|
||||
bench-pgsql: integrations.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
bench-pgsql: integrations.test generate-ini
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./integrations.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
|
||||
.PHONY: integration-test-coverage
|
||||
integration-test-coverage: integrations.cover.test generate-ini-mysql
|
||||
integration-test-coverage: integrations.cover.test generate-ini
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.cover.test -test.coverprofile=integration.coverage.out
|
||||
|
||||
integrations.mysql.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.mysql.test
|
||||
integrations.test: $(SOURCES)
|
||||
$(GO) test -c code.gitea.io/gitea/integrations -o integrations.test
|
||||
|
||||
integrations.mysql8.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.mysql8.test
|
||||
integrations.sqlite.test: $(SOURCES)
|
||||
$(GO) test -c code.gitea.io/gitea/integrations -o integrations.sqlite.test -tags 'sqlite sqlite_unlock_notify'
|
||||
|
||||
integrations.pgsql.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.pgsql.test
|
||||
integrations.cover.test: $(SOURCES)
|
||||
$(GO) test -c code.gitea.io/gitea/integrations -coverpkg $(shell echo $(PACKAGES) | tr ' ' ',') -o integrations.cover.test
|
||||
|
||||
integrations.mssql.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.mssql.test
|
||||
|
||||
integrations.sqlite.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.sqlite.test -tags 'sqlite sqlite_unlock_notify'
|
||||
|
||||
integrations.cover.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -coverpkg $(shell echo $(GO_PACKAGES) | tr ' ' ',') -o integrations.cover.test
|
||||
|
||||
.PHONY: migrations.mysql.test
|
||||
migrations.mysql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.mysql.test
|
||||
|
||||
.PHONY: migrations.mysql8.test
|
||||
migrations.mysql8.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.mysql8.test
|
||||
|
||||
.PHONY: migrations.pgsql.test
|
||||
migrations.pgsql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.pgsql.test
|
||||
|
||||
.PHONY: migrations.mssql.test
|
||||
migrations.mssql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.mssql.test
|
||||
.PHONY: migrations.test
|
||||
migrations.test: $(SOURCES)
|
||||
$(GO) test -c code.gitea.io/gitea/integrations/migration-test -o migrations.test
|
||||
|
||||
.PHONY: migrations.sqlite.test
|
||||
migrations.sqlite.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.sqlite.test -tags 'sqlite sqlite_unlock_notify'
|
||||
migrations.sqlite.test: $(SOURCES)
|
||||
$(GO) test -c code.gitea.io/gitea/integrations/migration-test -o migrations.sqlite.test -tags 'sqlite sqlite_unlock_notify'
|
||||
|
||||
.PHONY: check
|
||||
check: test
|
||||
|
||||
.PHONY: install $(TAGS_PREREQ)
|
||||
.PHONY: install
|
||||
install: $(wildcard *.go)
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
|
||||
$(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
|
||||
|
||||
.PHONY: build
|
||||
build: frontend backend
|
||||
build: $(EXECUTABLE)
|
||||
|
||||
.PHONY: frontend
|
||||
frontend: node-check $(FOMANTIC_DEST) $(WEBPACK_DEST)
|
||||
|
||||
.PHONY: backend
|
||||
backend: go-check generate $(EXECUTABLE)
|
||||
|
||||
.PHONY: generate
|
||||
generate: $(TAGS_PREREQ)
|
||||
CC= GOOS= GOARCH= $(GO) generate -mod=vendor -tags '$(TAGS)' $(GO_PACKAGES)
|
||||
|
||||
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build -mod=vendor $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||
$(EXECUTABLE): $(SOURCES)
|
||||
$(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||
|
||||
.PHONY: release
|
||||
release: frontend generate release-windows release-linux release-darwin release-copy release-compress release-sources release-docs release-check
|
||||
release: release-dirs release-windows release-linux release-darwin release-copy release-compress release-check
|
||||
|
||||
$(DIST_DIRS):
|
||||
mkdir -p $(DIST_DIRS)
|
||||
.PHONY: release-dirs
|
||||
release-dirs:
|
||||
mkdir -p $(DIST)/binaries $(DIST)/release
|
||||
|
||||
.PHONY: release-windows
|
||||
release-windows: | $(DIST_DIRS)
|
||||
release-windows:
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u src.techknowlogick.com/xgo; \
|
||||
$(GO) get -u src.techknowlogick.com/xgo; \
|
||||
fi
|
||||
@echo "Warning: windows version is built using golang 1.14"
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" GO111MODULE=off xgo -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||
xgo -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
|
||||
.PHONY: release-linux
|
||||
release-linux: | $(DIST_DIRS)
|
||||
release-linux:
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u src.techknowlogick.com/xgo; \
|
||||
$(GO) get -u src.techknowlogick.com/xgo; \
|
||||
fi
|
||||
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) .
|
||||
xgo -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/mips64le,linux/mips,linux/mipsle' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
|
||||
.PHONY: release-darwin
|
||||
release-darwin: | $(DIST_DIRS)
|
||||
release-darwin:
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u src.techknowlogick.com/xgo; \
|
||||
$(GO) get -u src.techknowlogick.com/xgo; \
|
||||
fi
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" GO111MODULE=off xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/amd64' -out gitea-$(VERSION) .
|
||||
xgo -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/*' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
|
||||
.PHONY: release-copy
|
||||
release-copy: | $(DIST_DIRS)
|
||||
release-copy:
|
||||
cd $(DIST); for file in `find /build -type f -name "*"`; do cp $${file} ./release/; done;
|
||||
|
||||
.PHONY: release-check
|
||||
release-check: | $(DIST_DIRS)
|
||||
release-check:
|
||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "checksumming $${file}" && $(SHASUM) `echo $${file} | sed 's/^..//'` > $${file}.sha256; done;
|
||||
|
||||
.PHONY: release-compress
|
||||
release-compress: | $(DIST_DIRS)
|
||||
release-compress:
|
||||
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u github.com/ulikunitz/xz/cmd/gxz; \
|
||||
$(GO) get -u github.com/ulikunitz/xz/cmd/gxz; \
|
||||
fi
|
||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
|
||||
|
||||
.PHONY: release-sources
|
||||
release-sources: | $(DIST_DIRS) node_modules
|
||||
echo $(VERSION) > $(STORED_VERSION_FILE)
|
||||
tar --exclude=./$(DIST) --exclude=./.git --exclude=./$(MAKE_EVIDENCE_DIR) --exclude=./node_modules/.cache --exclude=./$(AIR_TMP_DIR) -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz .
|
||||
rm -f $(STORED_VERSION_FILE)
|
||||
.PHONY: javascripts
|
||||
javascripts: public/js/index.js
|
||||
|
||||
.PHONY: release-docs
|
||||
release-docs: | $(DIST_DIRS) docs
|
||||
tar -czf $(DIST)/release/gitea-docs-$(VERSION).tar.gz -C ./docs/public .
|
||||
.IGNORE: public/js/index.js
|
||||
public/js/index.js: $(JAVASCRIPTS)
|
||||
cat $< >| $@
|
||||
|
||||
.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
|
||||
npm install --no-save
|
||||
@touch node_modules
|
||||
|
||||
.PHONY: npm-update
|
||||
npm-update: node-check | node_modules
|
||||
npx updates -cu
|
||||
rm -rf node_modules package-lock.json
|
||||
npm install --package-lock
|
||||
@touch node_modules
|
||||
|
||||
.PHONY: fomantic
|
||||
fomantic: $(FOMANTIC_DEST)
|
||||
|
||||
$(FOMANTIC_DEST): $(FOMANTIC_CONFIGS) | node_modules
|
||||
rm -rf $(FOMANTIC_DEST_DIR)
|
||||
cp -f web_src/fomantic/theme.config.less node_modules/fomantic-ui/src/theme.config
|
||||
cp -fr web_src/fomantic/_site/* node_modules/fomantic-ui/src/_site/
|
||||
npx gulp -f node_modules/fomantic-ui/gulpfile.js build
|
||||
@touch $(FOMANTIC_DEST)
|
||||
|
||||
.PHONY: webpack
|
||||
webpack: $(WEBPACK_DEST)
|
||||
|
||||
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json | node_modules
|
||||
rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||
npx webpack --hide-modules --display-entrypoints=false
|
||||
@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)); \
|
||||
.PHONY: stylesheets-check
|
||||
stylesheets-check: generate-stylesheets
|
||||
@diff=$$(git diff public/css/*); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make svg' and 'git add $(SVG_DEST_DIR)' and commit the result:"; \
|
||||
echo "Please run 'make generate-stylesheets' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi;
|
||||
|
||||
.PHONY: generate-stylesheets
|
||||
generate-stylesheets:
|
||||
@hash npx > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
echo "Please install npm version 5.2+"; \
|
||||
exit 1; \
|
||||
fi;
|
||||
$(eval BROWSERS := "> 1%, last 2 firefox versions, last 2 safari versions, ie 11")
|
||||
npx lessc --clean-css public/less/index.less public/css/index.css
|
||||
$(foreach file, $(filter-out public/less/themes/_base.less, $(wildcard public/less/themes/*)),npx lessc --clean-css public/less/themes/$(notdir $(file)) > public/css/theme-$(notdir $(call strip-suffix,$(file))).css;)
|
||||
$(foreach file, $(wildcard public/css/*),npx postcss --use autoprefixer --autoprefixer.browsers $(BROWSERS) -o $(file) $(file);)
|
||||
|
||||
.PHONY: swagger-ui
|
||||
swagger-ui:
|
||||
rm -Rf public/vendor/assets/swagger-ui
|
||||
git clone --depth=10 -b v3.13.4 --single-branch https://github.com/swagger-api/swagger-ui.git $(TMPDIR)/swagger-ui
|
||||
mv $(TMPDIR)/swagger-ui/dist public/vendor/assets/swagger-ui
|
||||
rm -Rf $(TMPDIR)/swagger-ui
|
||||
$(SED_INPLACE) "s;http://petstore.swagger.io/v2/swagger.json;../../../swagger.v1.json;g" public/vendor/assets/swagger-ui/index.html
|
||||
|
||||
.PHONY: update-translations
|
||||
update-translations:
|
||||
mkdir -p ./translations
|
||||
@@ -676,40 +411,33 @@ update-translations:
|
||||
mv ./translations/*.ini ./options/locale/
|
||||
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
|
||||
generate-images:
|
||||
npm install --no-save --no-package-lock xmldom fabric imagemin-zopfli
|
||||
node build/generate-images.js
|
||||
|
||||
.PHONY: pr\#%
|
||||
pr\#%: clean-all
|
||||
$(GO) run contrib/pr/checkout.go $*
|
||||
|
||||
.PHONY: golangci-lint
|
||||
golangci-lint:
|
||||
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
export BINARY="golangci-lint"; \
|
||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.31.0; \
|
||||
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
|
||||
endif
|
||||
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 $(TMPDIR)/images/128-raw.png $(TMPDIR)/images/128.png
|
||||
zopflipng $(TMPDIR)/images/64-raw.png $(TMPDIR)/images/64.png
|
||||
zopflipng $(TMPDIR)/images/32-raw.png $(TMPDIR)/images/32.png
|
||||
zopflipng $(TMPDIR)/images/16-raw.png $(TMPDIR)/images/16.png
|
||||
rm -f $(TMPDIR)/images/*-*.png
|
||||
convert $(TMPDIR)/images/16.png $(TMPDIR)/images/32.png \
|
||||
$(TMPDIR)/images/64.png $(TMPDIR)/images/128.png \
|
||||
$(PWD)/public/img/favicon.ico
|
||||
rm -rf $(TMPDIR)/images
|
||||
|
||||
.PHONY: pr
|
||||
pr:
|
||||
$(GO) run contrib/pr/checkout.go $(PR)
|
||||
|
41
README.md
41
README.md
@@ -1,19 +1,16 @@
|
||||
[简体中文](README_ZH.md)
|
||||
[简体中文](https://github.com/go-gitea/gitea/blob/master/README_ZH.md)
|
||||
|
||||
<h1> <img src="https://raw.githubusercontent.com/go-gitea/gitea/master/public/img/gitea-192.png" alt="logo" width="30" height="30"> Gitea - Git with a cup of tea</h1>
|
||||
# Gitea - Git with a cup of tea
|
||||
|
||||
[](https://drone.gitea.io/go-gitea/gitea)
|
||||
[](https://discord.gg/Gitea)
|
||||
[](https://drone.gitea.io/go-gitea/gitea)
|
||||
[](https://discord.gg/NsatcWJ)
|
||||
[](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)
|
||||
[](https://www.tickgit.com/browse?repo=github.com/go-gitea/gitea)
|
||||
[](https://opencollective.com/gitea)
|
||||
|
||||
## Purpose
|
||||
|
||||
@@ -32,22 +29,7 @@ This project has been
|
||||
|
||||
From the root of the source tree, run:
|
||||
|
||||
TAGS="bindata" make build
|
||||
|
||||
or if sqlite support is required:
|
||||
|
||||
TAGS="bindata sqlite sqlite_unlock_notify" make build
|
||||
|
||||
The `build` target is split into two sub-targets:
|
||||
|
||||
- `make backend` which requires [Go 1.13](https://golang.org/dl/) or greater.
|
||||
- `make frontend` which requires [Node.js 10.13](https://nodejs.org/en/download/) or greater.
|
||||
|
||||
If pre-built frontend files are present it is possible to only build the backend:
|
||||
|
||||
TAGS="bindata" make backend
|
||||
|
||||
Parallelism is not supported for these targets, so please don't include `-j <num>`.
|
||||
TAGS="bindata" make generate all
|
||||
|
||||
More info: https://docs.gitea.io/en-us/install-from-source/
|
||||
|
||||
@@ -72,7 +54,7 @@ NOTES:
|
||||
For more information and instructions about how to install Gitea, please look
|
||||
at our [documentation](https://docs.gitea.io/en-us/). If you have questions
|
||||
that are not covered by the documentation, you can get in contact with us on
|
||||
our [Discord server](https://discord.gg/Gitea),
|
||||
our [Discord server](https://discord.gg/NsatcWJ),
|
||||
or [forum](https://discourse.gitea.io/)!
|
||||
|
||||
## Authors
|
||||
@@ -121,8 +103,9 @@ for the full license text.
|
||||
## Screenshots
|
||||
Looking for an overview of the interface? Check it out!
|
||||
|
||||
||||
|
||||
| | | |
|
||||
|:---:|:---:|:---:|
|
||||
||||
|
||||
|||
|
||||
|||
|
||||
||||
|
||||
||||
|
||||
||||
|
||||
||||
|
||||
|
24
README_ZH.md
24
README_ZH.md
@@ -1,18 +1,15 @@
|
||||
[English](README.md)
|
||||
[English](https://github.com/go-gitea/gitea/blob/master/README.md)
|
||||
|
||||
<h1> <img src="https://raw.githubusercontent.com/go-gitea/gitea/master/public/img/gitea-192.png" alt="logo" width="30" height="30"> Gitea - Git with a cup of tea</h1>
|
||||
# Gitea - Git with a cup of tea
|
||||
|
||||
[](https://drone.gitea.io/go-gitea/gitea)
|
||||
[](https://discord.gg/Gitea)
|
||||
[](https://discord.gg/NsatcWJ)
|
||||
[](https://microbadger.com/images/gitea/gitea "Get your own image badge on microbadger.com")
|
||||
[](https://codecov.io/gh/go-gitea/gitea)
|
||||
[](https://coverage.gitea.io/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)
|
||||
[](https://opencollective.com/gitea)
|
||||
|
||||
## 目标
|
||||
|
||||
@@ -28,7 +25,7 @@ Gitea 的首要目标是创建一个极易安装,运行非常快速,安装
|
||||
|
||||
## 文档
|
||||
|
||||
关于如何安装请访问我们的 [文档站](https://docs.gitea.io/zh-cn/),如果没有找到对应的文档,你也可以通过 [Discord - 英文](https://discord.gg/gitea) 和 QQ群 328432459 来和我们交流。
|
||||
关于如何安装请访问我们的 [文档站](https://docs.gitea.io/zh-cn/),如果没有找到对应的文档,你也可以通过 [Discord - 英文](https://discord.gg/NsatcWJ) 和 QQ群 328432459 来和我们交流。
|
||||
|
||||
## 贡献流程
|
||||
|
||||
@@ -46,8 +43,9 @@ Fork -> Patch -> Push -> Pull Request
|
||||
|
||||
## 截图
|
||||
|
||||
||||
|
||||
| | | |
|
||||
|:---:|:---:|:---:|
|
||||
||||
|
||||
|||
|
||||
|||
|
||||
||||
|
||||
||||
|
||||
||||
|
||||
||||
|
||||
|
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.
|
File diff suppressed because one or more lines are too long
@@ -115,7 +115,6 @@
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer3"
|
||||
class="detail-remove"
|
||||
inkscape:label="Layer 3"
|
||||
style="display:inline">
|
||||
<g
|
||||
@@ -158,4 +157,4 @@
|
||||
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: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
32
build.go
32
build.go
@@ -1,32 +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.
|
||||
|
||||
//+build vendor
|
||||
|
||||
package main
|
||||
|
||||
// Libraries that are included to vendor utilities used during build.
|
||||
// These libraries will not be included in a normal compilation.
|
||||
|
||||
import (
|
||||
// for lint
|
||||
_ "github.com/mgechev/dots"
|
||||
_ "github.com/mgechev/revive/formatter"
|
||||
_ "github.com/mgechev/revive/lint"
|
||||
_ "github.com/mgechev/revive/rule"
|
||||
_ "github.com/mitchellh/go-homedir"
|
||||
_ "github.com/pelletier/go-toml"
|
||||
|
||||
// for embed
|
||||
_ "github.com/shurcooL/vfsgen"
|
||||
|
||||
// for cover merge
|
||||
_ "golang.org/x/tools/cover"
|
||||
|
||||
// for vet
|
||||
_ "code.gitea.io/gitea-vet"
|
||||
|
||||
// for swagger
|
||||
_ "github.com/go-swagger/go-swagger/cmd/swagger"
|
||||
)
|
@@ -1,86 +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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/shurcooL/vfsgen"
|
||||
)
|
||||
|
||||
func needsUpdate(dir string, filename string) (bool, []byte) {
|
||||
needRegen := false
|
||||
_, err := os.Stat(filename)
|
||||
if err != nil {
|
||||
needRegen = true
|
||||
}
|
||||
|
||||
oldHash, err := ioutil.ReadFile(filename + ".hash")
|
||||
if err != nil {
|
||||
oldHash = []byte{}
|
||||
}
|
||||
|
||||
hasher := sha1.New()
|
||||
|
||||
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _ = hasher.Write([]byte(info.Name()))
|
||||
_, _ = hasher.Write([]byte(info.ModTime().String()))
|
||||
_, _ = hasher.Write([]byte(strconv.FormatInt(info.Size(), 16)))
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return true, oldHash
|
||||
}
|
||||
|
||||
newHash := hasher.Sum([]byte{})
|
||||
|
||||
if bytes.Compare(oldHash, newHash) != 0 {
|
||||
|
||||
return true, newHash
|
||||
}
|
||||
|
||||
return needRegen, newHash
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 4 {
|
||||
log.Fatal("Insufficient number of arguments. Need: directory packageName filename")
|
||||
}
|
||||
|
||||
dir, packageName, filename := os.Args[1], os.Args[2], os.Args[3]
|
||||
|
||||
update, newHash := needsUpdate(dir, filename)
|
||||
|
||||
if !update {
|
||||
fmt.Printf("bindata for %s already up-to-date\n", packageName)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("generating bindata for %s\n", packageName)
|
||||
var fsTemplates http.FileSystem = http.Dir(dir)
|
||||
err := vfsgen.Generate(fsTemplates, vfsgen.Options{
|
||||
PackageName: packageName,
|
||||
BuildTags: "bindata",
|
||||
VariableName: "Assets",
|
||||
Filename: filename,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("%v\n", err)
|
||||
}
|
||||
_ = ioutil.WriteFile(filename+".hash", newHash, 0666)
|
||||
}
|
@@ -1,221 +0,0 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2015 Kenneth Shaw
|
||||
// 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 (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
gemojiURL = "https://raw.githubusercontent.com/github/gemoji/master/db/emoji.json"
|
||||
maxUnicodeVersion = 12
|
||||
)
|
||||
|
||||
var (
|
||||
flagOut = flag.String("o", "modules/emoji/emoji_data.go", "out")
|
||||
)
|
||||
|
||||
// Gemoji is a set of emoji data.
|
||||
type Gemoji []Emoji
|
||||
|
||||
// Emoji represents a single emoji and associated data.
|
||||
type Emoji struct {
|
||||
Emoji string `json:"emoji"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Aliases []string `json:"aliases"`
|
||||
UnicodeVersion string `json:"unicode_version,omitempty"`
|
||||
SkinTones bool `json:"skin_tones,omitempty"`
|
||||
}
|
||||
|
||||
// Don't include some fields in JSON
|
||||
func (e Emoji) MarshalJSON() ([]byte, error) {
|
||||
type emoji Emoji
|
||||
x := emoji(e)
|
||||
x.UnicodeVersion = ""
|
||||
x.Description = ""
|
||||
x.SkinTones = false
|
||||
return json.Marshal(x)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
|
||||
flag.Parse()
|
||||
|
||||
// generate data
|
||||
buf, err := generate()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// write
|
||||
err = ioutil.WriteFile(*flagOut, buf, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
var replacer = strings.NewReplacer(
|
||||
"main.Gemoji", "Gemoji",
|
||||
"main.Emoji", "\n",
|
||||
"}}", "},\n}",
|
||||
", Description:", ", ",
|
||||
", Aliases:", ", ",
|
||||
", UnicodeVersion:", ", ",
|
||||
", SkinTones:", ", ",
|
||||
)
|
||||
|
||||
var emojiRE = regexp.MustCompile(`\{Emoji:"([^"]*)"`)
|
||||
|
||||
func generate() ([]byte, error) {
|
||||
var err error
|
||||
|
||||
// load gemoji data
|
||||
res, err := http.Get(gemojiURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
// read all
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// unmarshal
|
||||
var data Gemoji
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var skinTones = make(map[string]string)
|
||||
|
||||
skinTones["\U0001f3fb"] = "Light Skin Tone"
|
||||
skinTones["\U0001f3fc"] = "Medium-Light Skin Tone"
|
||||
skinTones["\U0001f3fd"] = "Medium Skin Tone"
|
||||
skinTones["\U0001f3fe"] = "Medium-Dark Skin Tone"
|
||||
skinTones["\U0001f3ff"] = "Dark Skin Tone"
|
||||
|
||||
var tmp Gemoji
|
||||
|
||||
//filter out emoji that require greater than max unicode version
|
||||
for i := range data {
|
||||
val, _ := strconv.ParseFloat(data[i].UnicodeVersion, 64)
|
||||
if int(val) <= maxUnicodeVersion {
|
||||
tmp = append(tmp, data[i])
|
||||
}
|
||||
}
|
||||
data = tmp
|
||||
|
||||
sort.Slice(data, func(i, j int) bool {
|
||||
return data[i].Aliases[0] < data[j].Aliases[0]
|
||||
})
|
||||
|
||||
aliasMap := make(map[string]int, len(data))
|
||||
|
||||
for i, e := range data {
|
||||
if e.Emoji == "" || len(e.Aliases) == 0 {
|
||||
continue
|
||||
}
|
||||
for _, a := range e.Aliases {
|
||||
if a == "" {
|
||||
continue
|
||||
}
|
||||
aliasMap[a] = i
|
||||
}
|
||||
}
|
||||
|
||||
// gitea customizations
|
||||
i, ok := aliasMap["tada"]
|
||||
if ok {
|
||||
data[i].Aliases = append(data[i].Aliases, "hooray")
|
||||
}
|
||||
i, ok = aliasMap["laughing"]
|
||||
if ok {
|
||||
data[i].Aliases = append(data[i].Aliases, "laugh")
|
||||
}
|
||||
|
||||
// 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)
|
||||
_ = ioutil.WriteFile("assets/emoji.json", file, 0644)
|
||||
|
||||
// Add skin tones to emoji that support it
|
||||
var (
|
||||
s []string
|
||||
newEmoji string
|
||||
newDescription string
|
||||
newData Emoji
|
||||
)
|
||||
|
||||
for i := range data {
|
||||
if data[i].SkinTones {
|
||||
for k, v := range skinTones {
|
||||
s = strings.Split(data[i].Emoji, "")
|
||||
|
||||
if utf8.RuneCountInString(data[i].Emoji) == 1 {
|
||||
s = append(s, k)
|
||||
} else {
|
||||
// insert into slice after first element because all emoji that support skin tones
|
||||
// have that modifer placed at this spot
|
||||
s = append(s, "")
|
||||
copy(s[2:], s[1:])
|
||||
s[1] = k
|
||||
}
|
||||
|
||||
newEmoji = strings.Join(s, "")
|
||||
newDescription = data[i].Description + ": " + v
|
||||
newAlias := data[i].Aliases[0] + "_" + strings.ReplaceAll(v, " ", "_")
|
||||
|
||||
newData = Emoji{newEmoji, newDescription, []string{newAlias}, "12.0", false}
|
||||
data = append(data, newData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add header
|
||||
str := replacer.Replace(fmt.Sprintf(hdr, gemojiURL, data))
|
||||
|
||||
// change the format of the unicode string
|
||||
str = emojiRE.ReplaceAllStringFunc(str, func(s string) string {
|
||||
var err error
|
||||
s, err = strconv.Unquote(s[len("{Emoji:"):])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return "{" + strconv.QuoteToASCII(s)
|
||||
})
|
||||
|
||||
// format
|
||||
return format.Source([]byte(str))
|
||||
}
|
||||
|
||||
const hdr = `
|
||||
// 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 emoji
|
||||
|
||||
// Code generated by gen.go. DO NOT EDIT.
|
||||
// Sourced from %s
|
||||
//
|
||||
var GemojiData = %#v
|
||||
`
|
@@ -1,101 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
const imageminZopfli = require('imagemin-zopfli');
|
||||
const {fabric} = require('fabric');
|
||||
const {DOMParser, XMLSerializer} = require('xmldom');
|
||||
const {readFile, writeFile} = require('fs').promises;
|
||||
const {resolve} = require('path');
|
||||
const Svgo = require('svgo');
|
||||
|
||||
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 generateSvgFavicon(svg, outputFile) {
|
||||
const svgo = new Svgo({
|
||||
plugins: [
|
||||
{removeDimensions: true},
|
||||
{
|
||||
addAttributesToSVGElement: {
|
||||
attributes: [
|
||||
{'width': '32'},
|
||||
{'height': '32'},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const {data} = await svgo.optimize(svg);
|
||||
await writeFile(outputFile, data);
|
||||
}
|
||||
|
||||
async function generate(svg, outputFile, {size, bg, removeDetail} = {}) {
|
||||
const parser = new DOMParser();
|
||||
const serializer = new XMLSerializer();
|
||||
const document = parser.parseFromString(svg);
|
||||
|
||||
if (removeDetail) {
|
||||
for (const el of Array.from(document.getElementsByTagName('g') || [])) {
|
||||
for (const attribute of Array.from(el.attributes || [])) {
|
||||
if (attribute.name === 'class' && attribute.value === 'detail-remove') {
|
||||
el.parentNode.removeChild(el);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svg = serializer.serializeToString(document);
|
||||
|
||||
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 svg = await readFile(resolve(__dirname, '../assets/logo.svg'), 'utf8');
|
||||
await generateSvgFavicon(svg, resolve(__dirname, '../public/img/favicon.svg'));
|
||||
await generate(svg, resolve(__dirname, '../public/img/gitea-lg.png'), {size: 880});
|
||||
await generate(svg, resolve(__dirname, '../public/img/gitea-512.png'), {size: 512});
|
||||
await generate(svg, resolve(__dirname, '../public/img/gitea-192.png'), {size: 192});
|
||||
await generate(svg, resolve(__dirname, '../public/img/gitea-sm.png'), {size: 120});
|
||||
await generate(svg, resolve(__dirname, '../public/img/avatar_default.png'), {size: 200});
|
||||
await generate(svg, resolve(__dirname, '../public/img/favicon.png'), {size: 180, removeDetail: true});
|
||||
await generate(svg, resolve(__dirname, '../public/img/apple-touch-icon.png'), {size: 180, bg: true});
|
||||
}
|
||||
|
||||
main().then(exit).catch(exit);
|
||||
|
@@ -1,118 +0,0 @@
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
prefix = "gitea-licenses"
|
||||
url = "https://api.github.com/repos/spdx/license-list-data/tarball"
|
||||
githubApiToken = ""
|
||||
githubUsername = ""
|
||||
destination = ""
|
||||
)
|
||||
|
||||
flag.StringVar(&destination, "dest", "options/license/", "destination for the licenses")
|
||||
flag.StringVar(&githubUsername, "username", "", "github username")
|
||||
flag.StringVar(&githubApiToken, "token", "", "github api token")
|
||||
flag.Parse()
|
||||
|
||||
file, err := ioutil.TempFile(os.TempDir(), prefix)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create temp file. %s", err)
|
||||
}
|
||||
|
||||
defer util.Remove(file.Name())
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to download archive. %s", err)
|
||||
}
|
||||
|
||||
if len(githubApiToken) > 0 && len(githubUsername) > 0 {
|
||||
req.SetBasicAuth(githubUsername, githubApiToken)
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to download archive. %s", err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if _, err := io.Copy(file, resp.Body); err != nil {
|
||||
log.Fatalf("Failed to copy archive to file. %s", err)
|
||||
}
|
||||
|
||||
if _, err := file.Seek(0, 0); err != nil {
|
||||
log.Fatalf("Failed to reset seek on archive. %s", err)
|
||||
}
|
||||
|
||||
gz, err := gzip.NewReader(file)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to gunzip the archive. %s", err)
|
||||
}
|
||||
|
||||
tr := tar.NewReader(gz)
|
||||
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to iterate archive. %s", err)
|
||||
}
|
||||
|
||||
if !strings.Contains(hdr.Name, "/text/") {
|
||||
continue
|
||||
}
|
||||
|
||||
if filepath.Ext(hdr.Name) != ".txt" {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(filepath.Base(hdr.Name), "README") {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(filepath.Base(hdr.Name), "deprecated_") {
|
||||
continue
|
||||
}
|
||||
out, err := os.Create(path.Join(destination, strings.TrimSuffix(filepath.Base(hdr.Name), ".txt")))
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create new file. %s", err)
|
||||
}
|
||||
|
||||
defer out.Close()
|
||||
|
||||
if _, err := io.Copy(out, tr); err != nil {
|
||||
log.Fatalf("Failed to write new file. %s", err)
|
||||
} else {
|
||||
fmt.Printf("Written %s\n", out.Name())
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("Done")
|
||||
}
|
@@ -1,65 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
const fastGlob = require('fast-glob');
|
||||
const Svgo = require('svgo');
|
||||
const {resolve, parse} = require('path');
|
||||
const {readFile, writeFile, mkdir} = require('fs').promises;
|
||||
|
||||
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 = ''} = {}) {
|
||||
let name = parse(file).name;
|
||||
if (prefix) name = `${prefix}-${name}`;
|
||||
if (prefix === 'octicon') name = name.replace(/-[0-9]+$/, ''); // chop of '-16' on octicons
|
||||
|
||||
const svgo = new Svgo({
|
||||
plugins: [
|
||||
{removeXMLNS: true},
|
||||
{removeDimensions: true},
|
||||
{
|
||||
addClassesToSVGElement: {
|
||||
classNames: [
|
||||
'svg',
|
||||
name,
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
addAttributesToSVGElement: {
|
||||
attributes: [
|
||||
{'width': '16'},
|
||||
{'height': '16'},
|
||||
{'aria-hidden': 'true'},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const {data} = await svgo.optimize(await readFile(file, 'utf8'));
|
||||
await writeFile(resolve(outputDir, `${name}.svg`), data);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
await mkdir(outputDir);
|
||||
} catch {}
|
||||
|
||||
for (const file of glob('../node_modules/@primer/octicons/build/svg/*-16.svg')) {
|
||||
await processFile(file, {prefix: 'octicon'});
|
||||
}
|
||||
|
||||
for (const file of glob('../web_src/svg/*.svg')) {
|
||||
await processFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
main().then(exit).catch(exit);
|
||||
|
@@ -1,119 +0,0 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Copyright (c) 2015, Wade Simmons
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// gocovmerge takes the results from multiple `go test -coverprofile` runs and
|
||||
// merges them into one profile
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"golang.org/x/tools/cover"
|
||||
)
|
||||
|
||||
func mergeProfiles(p *cover.Profile, merge *cover.Profile) {
|
||||
if p.Mode != merge.Mode {
|
||||
log.Fatalf("cannot merge profiles with different modes")
|
||||
}
|
||||
// Since the blocks are sorted, we can keep track of where the last block
|
||||
// was inserted and only look at the blocks after that as targets for merge
|
||||
startIndex := 0
|
||||
for _, b := range merge.Blocks {
|
||||
startIndex = mergeProfileBlock(p, b, startIndex)
|
||||
}
|
||||
}
|
||||
|
||||
func mergeProfileBlock(p *cover.Profile, pb cover.ProfileBlock, startIndex int) int {
|
||||
sortFunc := func(i int) bool {
|
||||
pi := p.Blocks[i+startIndex]
|
||||
return pi.StartLine >= pb.StartLine && (pi.StartLine != pb.StartLine || pi.StartCol >= pb.StartCol)
|
||||
}
|
||||
|
||||
i := 0
|
||||
if sortFunc(i) != true {
|
||||
i = sort.Search(len(p.Blocks)-startIndex, sortFunc)
|
||||
}
|
||||
i += startIndex
|
||||
if i < len(p.Blocks) && p.Blocks[i].StartLine == pb.StartLine && p.Blocks[i].StartCol == pb.StartCol {
|
||||
if p.Blocks[i].EndLine != pb.EndLine || p.Blocks[i].EndCol != pb.EndCol {
|
||||
log.Fatalf("OVERLAP MERGE: %v %v %v", p.FileName, p.Blocks[i], pb)
|
||||
}
|
||||
switch p.Mode {
|
||||
case "set":
|
||||
p.Blocks[i].Count |= pb.Count
|
||||
case "count", "atomic":
|
||||
p.Blocks[i].Count += pb.Count
|
||||
default:
|
||||
log.Fatalf("unsupported covermode: '%s'", p.Mode)
|
||||
}
|
||||
} else {
|
||||
if i > 0 {
|
||||
pa := p.Blocks[i-1]
|
||||
if pa.EndLine >= pb.EndLine && (pa.EndLine != pb.EndLine || pa.EndCol > pb.EndCol) {
|
||||
log.Fatalf("OVERLAP BEFORE: %v %v %v", p.FileName, pa, pb)
|
||||
}
|
||||
}
|
||||
if i < len(p.Blocks)-1 {
|
||||
pa := p.Blocks[i+1]
|
||||
if pa.StartLine <= pb.StartLine && (pa.StartLine != pb.StartLine || pa.StartCol < pb.StartCol) {
|
||||
log.Fatalf("OVERLAP AFTER: %v %v %v", p.FileName, pa, pb)
|
||||
}
|
||||
}
|
||||
p.Blocks = append(p.Blocks, cover.ProfileBlock{})
|
||||
copy(p.Blocks[i+1:], p.Blocks[i:])
|
||||
p.Blocks[i] = pb
|
||||
}
|
||||
return i + 1
|
||||
}
|
||||
|
||||
func addProfile(profiles []*cover.Profile, p *cover.Profile) []*cover.Profile {
|
||||
i := sort.Search(len(profiles), func(i int) bool { return profiles[i].FileName >= p.FileName })
|
||||
if i < len(profiles) && profiles[i].FileName == p.FileName {
|
||||
mergeProfiles(profiles[i], p)
|
||||
} else {
|
||||
profiles = append(profiles, nil)
|
||||
copy(profiles[i+1:], profiles[i:])
|
||||
profiles[i] = p
|
||||
}
|
||||
return profiles
|
||||
}
|
||||
|
||||
func dumpProfiles(profiles []*cover.Profile, out io.Writer) {
|
||||
if len(profiles) == 0 {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(out, "mode: %s\n", profiles[0].Mode)
|
||||
for _, p := range profiles {
|
||||
for _, b := range p.Blocks {
|
||||
fmt.Fprintf(out, "%s:%d.%d,%d.%d %d %d\n", p.FileName, b.StartLine, b.StartCol, b.EndLine, b.EndCol, b.NumStmt, b.Count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
var merged []*cover.Profile
|
||||
|
||||
for _, file := range flag.Args() {
|
||||
profiles, err := cover.ParseProfiles(file)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to parse profiles: %v", err)
|
||||
}
|
||||
for _, p := range profiles {
|
||||
merged = addProfile(merged, p)
|
||||
}
|
||||
}
|
||||
|
||||
dumpProfiles(merged, os.Stdout)
|
||||
}
|
325
build/lint.go
325
build/lint.go
@@ -1,325 +0,0 @@
|
||||
// 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/mgechev/dots"
|
||||
"github.com/mgechev/revive/formatter"
|
||||
"github.com/mgechev/revive/lint"
|
||||
"github.com/mgechev/revive/rule"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/pelletier/go-toml"
|
||||
)
|
||||
|
||||
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.Unmarshal(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)
|
||||
}
|
@@ -1,23 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
mv ./options/locale/locale_en-US.ini ./options/
|
||||
|
||||
# Make sure to only change lines that have the translation enclosed between quotes
|
||||
sed -i -r -e '/^[a-zA-Z0-9_.-]+[ ]*=[ ]*".*"$/ {
|
||||
s/^([a-zA-Z0-9_.-]+)[ ]*="/\1=/
|
||||
s/\\"/"/g
|
||||
s/"$//
|
||||
}' ./options/locale/*.ini
|
||||
|
||||
# Remove translation under 25% of en_us
|
||||
baselines=`wc -l "./options/locale_en-US.ini" | cut -d" " -f1`
|
||||
baselines=$((baselines / 4))
|
||||
for filename in ./options/locale/*.ini; do
|
||||
lines=`wc -l "$filename" | cut -d" " -f1`
|
||||
if [ $lines -lt $baselines ]; then
|
||||
echo "Removing $filename: $lines/$baselines"
|
||||
rm "$filename"
|
||||
fi
|
||||
done
|
||||
|
||||
mv ./options/locale_en-US.ini ./options/locale/
|
217
cmd/admin.go
217
cmd/admin.go
@@ -6,19 +6,16 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
"code.gitea.io/git"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/auth/oauth2"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/generate"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
pwd "code.gitea.io/gitea/modules/password"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
@@ -45,10 +42,6 @@ var (
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "Username. DEPRECATED: use username instead",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "username",
|
||||
Usage: "Username",
|
||||
},
|
||||
cli.StringFlag{
|
||||
@@ -63,23 +56,24 @@ var (
|
||||
Name: "admin",
|
||||
Usage: "User is an admin",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "config, c",
|
||||
Value: "custom/conf/app.ini",
|
||||
Usage: "Custom configuration file path",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "random-password",
|
||||
Usage: "Generate a random password for the user",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "must-change-password",
|
||||
Usage: "Set this option to false to prevent forcing the user to change their password after initial login, (Default: true)",
|
||||
Usage: "Force the user to change his/her password after initial login",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "random-password-length",
|
||||
Usage: "Length of the random password to be generated",
|
||||
Value: 12,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "access-token",
|
||||
Usage: "Generate access token for the user",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -98,6 +92,11 @@ var (
|
||||
Value: "",
|
||||
Usage: "New password to set for user",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "config, c",
|
||||
Value: "custom/conf/app.ini",
|
||||
Usage: "Custom configuration file path",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -120,12 +119,26 @@ var (
|
||||
Name: "hooks",
|
||||
Usage: "Regenerate git-hooks",
|
||||
Action: runRegenerateHooks,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "config, c",
|
||||
Value: "custom/conf/app.ini",
|
||||
Usage: "Custom configuration file path",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
microcmdRegenKeys = cli.Command{
|
||||
Name: "keys",
|
||||
Usage: "Regenerate authorized_keys file",
|
||||
Action: runRegenerateKeys,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "config, c",
|
||||
Value: "custom/conf/app.ini",
|
||||
Usage: "Custom configuration file path",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
subcmdAuth = cli.Command{
|
||||
@@ -134,10 +147,6 @@ var (
|
||||
Subcommands: []cli.Command{
|
||||
microcmdAuthAddOauth,
|
||||
microcmdAuthUpdateOauth,
|
||||
cmdAuthAddLdapBindDn,
|
||||
cmdAuthUpdateLdapBindDn,
|
||||
cmdAuthAddLdapSimpleAuth,
|
||||
cmdAuthUpdateLdapSimpleAuth,
|
||||
microcmdAuthList,
|
||||
microcmdAuthDelete,
|
||||
},
|
||||
@@ -148,46 +157,39 @@ var (
|
||||
Usage: "List auth sources",
|
||||
Action: runListAuth,
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{
|
||||
Name: "min-width",
|
||||
Usage: "Minimal cell width including any padding for the formatted table",
|
||||
Value: 0,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "tab-width",
|
||||
Usage: "width of tab characters in formatted table (equivalent number of spaces)",
|
||||
Value: 8,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "padding",
|
||||
Usage: "padding added to a cell before computing its width",
|
||||
Value: 1,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "pad-char",
|
||||
Usage: `ASCII char used for padding if padchar == '\\t', the Writer will assume that the width of a '\\t' in the formatted output is tabwidth, and cells are left-aligned independent of align_left (for correct-looking results, tabwidth must correspond to the tab width in the viewer displaying the result)`,
|
||||
Value: "\t",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "vertical-bars",
|
||||
Usage: "Set to true to print vertical bars between columns",
|
||||
Name: "config, c",
|
||||
Value: "custom/conf/app.ini",
|
||||
Usage: "Custom configuration file path",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
idFlag = cli.Int64Flag{
|
||||
Name: "id",
|
||||
Usage: "ID of authentication source",
|
||||
Usage: "ID of OAuth authentication source",
|
||||
}
|
||||
|
||||
microcmdAuthDelete = cli.Command{
|
||||
Name: "delete",
|
||||
Usage: "Delete specific auth source",
|
||||
Flags: []cli.Flag{idFlag},
|
||||
Action: runDeleteAuth,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "config, c",
|
||||
Value: "custom/conf/app.ini",
|
||||
Usage: "Custom configuration file path",
|
||||
},
|
||||
idFlag,
|
||||
},
|
||||
}
|
||||
|
||||
oauthCLIFlags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "config, c",
|
||||
Value: "custom/conf/app.ini",
|
||||
Usage: "Custom configuration file path",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "name",
|
||||
Value: "",
|
||||
@@ -260,19 +262,14 @@ func runChangePassword(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
if !pwd.IsComplexEnough(c.String("password")) {
|
||||
return errors.New("Password does not meet complexity requirements")
|
||||
}
|
||||
pwned, err := pwd.IsPwned(context.Background(), c.String("password"))
|
||||
if err != nil {
|
||||
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")
|
||||
user, err := models.GetUserByName(uname)
|
||||
if err != nil {
|
||||
@@ -282,8 +279,7 @@ func runChangePassword(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
user.HashPassword(c.String("password"))
|
||||
|
||||
if err := models.UpdateUserCols(user, "passwd", "passwd_hash_algo", "salt"); err != nil {
|
||||
if err := models.UpdateUserCols(user, "passwd", "salt"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -292,47 +288,38 @@ func runChangePassword(c *cli.Context) error {
|
||||
}
|
||||
|
||||
func runCreateUser(c *cli.Context) error {
|
||||
if err := argsSet(c, "email"); err != nil {
|
||||
if err := argsSet(c, "name", "email"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.IsSet("name") && c.IsSet("username") {
|
||||
return errors.New("Cannot set both --name and --username flags")
|
||||
}
|
||||
if !c.IsSet("name") && !c.IsSet("username") {
|
||||
return errors.New("One of --name or --username flags must be set")
|
||||
}
|
||||
|
||||
if c.IsSet("password") && c.IsSet("random-password") {
|
||||
return errors.New("cannot set both -random-password and -password flags")
|
||||
}
|
||||
|
||||
var username string
|
||||
if c.IsSet("username") {
|
||||
username = c.String("username")
|
||||
} else {
|
||||
username = c.String("name")
|
||||
fmt.Fprintf(os.Stderr, "--name flag is deprecated. Use --username instead.\n")
|
||||
}
|
||||
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var password string
|
||||
|
||||
if c.IsSet("password") {
|
||||
password = c.String("password")
|
||||
} else if c.IsSet("random-password") {
|
||||
var err error
|
||||
password, err = pwd.Generate(c.Int("random-password-length"))
|
||||
password, err = generate.GetRandomString(c.Int("random-password-length"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("generated random password is '%s'\n", password)
|
||||
} else {
|
||||
return errors.New("must set either password or random-password flag")
|
||||
}
|
||||
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// always default to true
|
||||
var changePassword = true
|
||||
|
||||
@@ -346,34 +333,19 @@ func runCreateUser(c *cli.Context) error {
|
||||
changePassword = c.Bool("must-change-password")
|
||||
}
|
||||
|
||||
u := &models.User{
|
||||
Name: username,
|
||||
if err := models.CreateUser(&models.User{
|
||||
Name: c.String("name"),
|
||||
Email: c.String("email"),
|
||||
Passwd: password,
|
||||
IsActive: true,
|
||||
IsAdmin: c.Bool("admin"),
|
||||
MustChangePassword: changePassword,
|
||||
Theme: setting.UI.DefaultTheme,
|
||||
}
|
||||
|
||||
if err := models.CreateUser(u); err != nil {
|
||||
}); err != nil {
|
||||
return fmt.Errorf("CreateUser: %v", err)
|
||||
}
|
||||
|
||||
if c.Bool("access-token") {
|
||||
t := &models.AccessToken{
|
||||
Name: "gitea-admin",
|
||||
UID: u.ID,
|
||||
}
|
||||
|
||||
if err := models.NewAccessToken(t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Access token was successfully created... %s\n", t.Token)
|
||||
}
|
||||
|
||||
fmt.Printf("New user '%s' has been successfully created!\n", username)
|
||||
fmt.Printf("New user '%s' has been successfully created!\n", c.String("name"))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -385,11 +357,9 @@ func runRepoSyncReleases(c *cli.Context) error {
|
||||
log.Trace("Synchronizing repository releases (this may take a while)")
|
||||
for page := 1; ; page++ {
|
||||
repos, count, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
|
||||
ListOptions: models.ListOptions{
|
||||
PageSize: models.RepositoryListDefaultPageSize,
|
||||
Page: page,
|
||||
},
|
||||
Private: true,
|
||||
Page: page,
|
||||
PageSize: models.RepositoryListDefaultPageSize,
|
||||
Private: true,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("SearchRepositoryByName: %v", err)
|
||||
@@ -412,22 +382,19 @@ func runRepoSyncReleases(c *cli.Context) error {
|
||||
}
|
||||
log.Trace(" currentNumReleases is %d, running SyncReleasesWithTags", oldnum)
|
||||
|
||||
if err = repo_module.SyncReleasesWithTags(repo, gitRepo); err != nil {
|
||||
if err = models.SyncReleasesWithTags(repo, gitRepo); err != nil {
|
||||
log.Warn(" SyncReleasesWithTags: %v", err)
|
||||
gitRepo.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
count, err = getReleaseCount(repo.ID)
|
||||
if err != nil {
|
||||
log.Warn(" GetReleaseCountByRepoID: %v", err)
|
||||
gitRepo.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
log.Trace(" repo %s releases synchronized to tags: from %d to %d",
|
||||
repo.FullName(), oldnum, count)
|
||||
gitRepo.Close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,13 +411,21 @@ func getReleaseCount(id int64) (int64, error) {
|
||||
}
|
||||
|
||||
func runRegenerateHooks(c *cli.Context) error {
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
return repo_module.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
|
||||
return models.SyncRepositoryHooks()
|
||||
}
|
||||
|
||||
func runRegenerateKeys(c *cli.Context) error {
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -479,6 +454,10 @@ func parseOAuth2Config(c *cli.Context) *models.OAuth2Config {
|
||||
}
|
||||
|
||||
func runAddOauth(c *cli.Context) error {
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -492,6 +471,10 @@ func runAddOauth(c *cli.Context) error {
|
||||
}
|
||||
|
||||
func runUpdateOauth(c *cli.Context) error {
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
|
||||
if !c.IsSet("id") {
|
||||
return fmt.Errorf("--id flag is missing")
|
||||
}
|
||||
@@ -528,7 +511,7 @@ func runUpdateOauth(c *cli.Context) error {
|
||||
}
|
||||
|
||||
// update custom URL mapping
|
||||
var customURLMapping = &oauth2.CustomURLMapping{}
|
||||
var customURLMapping *oauth2.CustomURLMapping
|
||||
|
||||
if oAuth2Config.CustomURLMapping != nil {
|
||||
customURLMapping.TokenURL = oAuth2Config.CustomURLMapping.TokenURL
|
||||
@@ -559,6 +542,10 @@ func runUpdateOauth(c *cli.Context) error {
|
||||
}
|
||||
|
||||
func runListAuth(c *cli.Context) error {
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -569,21 +556,11 @@ func runListAuth(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
flags := tabwriter.AlignRight
|
||||
if c.Bool("vertical-bars") {
|
||||
flags |= tabwriter.Debug
|
||||
}
|
||||
|
||||
padChar := byte('\t')
|
||||
if len(c.String("pad-char")) > 0 {
|
||||
padChar = c.String("pad-char")[0]
|
||||
}
|
||||
|
||||
// loop through each source and print
|
||||
w := tabwriter.NewWriter(os.Stdout, c.Int("min-width"), c.Int("tab-width"), c.Int("padding"), padChar, flags)
|
||||
fmt.Fprintf(w, "ID\tName\tType\tEnabled\n")
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.AlignRight)
|
||||
fmt.Fprintf(w, "ID\tName\tType\tEnabled")
|
||||
for _, source := range loginSources {
|
||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, models.LoginNames[source.Type], source.IsActived)
|
||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t", source.ID, source.Name, models.LoginNames[source.Type], source.IsActived)
|
||||
}
|
||||
w.Flush()
|
||||
|
||||
@@ -591,6 +568,10 @@ func runListAuth(c *cli.Context) error {
|
||||
}
|
||||
|
||||
func runDeleteAuth(c *cli.Context) error {
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
|
||||
if !c.IsSet("id") {
|
||||
return fmt.Errorf("--id flag is missing")
|
||||
}
|
||||
|
@@ -1,373 +0,0 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/auth/ldap"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
type (
|
||||
authService struct {
|
||||
initDB func() error
|
||||
createLoginSource func(loginSource *models.LoginSource) error
|
||||
updateLoginSource func(loginSource *models.LoginSource) error
|
||||
getLoginSourceByID func(id int64) (*models.LoginSource, error)
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
commonLdapCLIFlags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "name",
|
||||
Usage: "Authentication name.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "not-active",
|
||||
Usage: "Deactivate the authentication source.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "security-protocol",
|
||||
Usage: "Security protocol name.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "skip-tls-verify",
|
||||
Usage: "Disable TLS verification.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "host",
|
||||
Usage: "The address where the LDAP server can be reached.",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "port",
|
||||
Usage: "The port to use when connecting to the LDAP server.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "user-search-base",
|
||||
Usage: "The LDAP base at which user accounts will be searched for.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "user-filter",
|
||||
Usage: "An LDAP filter declaring how to find the user record that is attempting to authenticate.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "admin-filter",
|
||||
Usage: "An LDAP filter specifying if a user should be given administrator privileges.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "restricted-filter",
|
||||
Usage: "An LDAP filter specifying if a user should be given restricted status.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "allow-deactivate-all",
|
||||
Usage: "Allow empty search results to deactivate all users.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "username-attribute",
|
||||
Usage: "The attribute of the user’s LDAP record containing the user name.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "firstname-attribute",
|
||||
Usage: "The attribute of the user’s LDAP record containing the user’s first name.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "surname-attribute",
|
||||
Usage: "The attribute of the user’s LDAP record containing the user’s surname.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "email-attribute",
|
||||
Usage: "The attribute of the user’s LDAP record containing the user’s email address.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "public-ssh-key-attribute",
|
||||
Usage: "The attribute of the user’s LDAP record containing the user’s public ssh key.",
|
||||
},
|
||||
}
|
||||
|
||||
ldapBindDnCLIFlags = append(commonLdapCLIFlags,
|
||||
cli.StringFlag{
|
||||
Name: "bind-dn",
|
||||
Usage: "The DN to bind to the LDAP server with when searching for the user.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "bind-password",
|
||||
Usage: "The password for the Bind DN, if any.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "attributes-in-bind",
|
||||
Usage: "Fetch attributes in bind DN context.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "synchronize-users",
|
||||
Usage: "Enable user synchronization.",
|
||||
},
|
||||
cli.UintFlag{
|
||||
Name: "page-size",
|
||||
Usage: "Search page size.",
|
||||
})
|
||||
|
||||
ldapSimpleAuthCLIFlags = append(commonLdapCLIFlags,
|
||||
cli.StringFlag{
|
||||
Name: "user-dn",
|
||||
Usage: "The user’s DN.",
|
||||
})
|
||||
|
||||
cmdAuthAddLdapBindDn = cli.Command{
|
||||
Name: "add-ldap",
|
||||
Usage: "Add new LDAP (via Bind DN) authentication source",
|
||||
Action: func(c *cli.Context) error {
|
||||
return newAuthService().addLdapBindDn(c)
|
||||
},
|
||||
Flags: ldapBindDnCLIFlags,
|
||||
}
|
||||
|
||||
cmdAuthUpdateLdapBindDn = cli.Command{
|
||||
Name: "update-ldap",
|
||||
Usage: "Update existing LDAP (via Bind DN) authentication source",
|
||||
Action: func(c *cli.Context) error {
|
||||
return newAuthService().updateLdapBindDn(c)
|
||||
},
|
||||
Flags: append([]cli.Flag{idFlag}, ldapBindDnCLIFlags...),
|
||||
}
|
||||
|
||||
cmdAuthAddLdapSimpleAuth = cli.Command{
|
||||
Name: "add-ldap-simple",
|
||||
Usage: "Add new LDAP (simple auth) authentication source",
|
||||
Action: func(c *cli.Context) error {
|
||||
return newAuthService().addLdapSimpleAuth(c)
|
||||
},
|
||||
Flags: ldapSimpleAuthCLIFlags,
|
||||
}
|
||||
|
||||
cmdAuthUpdateLdapSimpleAuth = cli.Command{
|
||||
Name: "update-ldap-simple",
|
||||
Usage: "Update existing LDAP (simple auth) authentication source",
|
||||
Action: func(c *cli.Context) error {
|
||||
return newAuthService().updateLdapSimpleAuth(c)
|
||||
},
|
||||
Flags: append([]cli.Flag{idFlag}, ldapSimpleAuthCLIFlags...),
|
||||
}
|
||||
)
|
||||
|
||||
// newAuthService creates a service with default functions.
|
||||
func newAuthService() *authService {
|
||||
return &authService{
|
||||
initDB: initDB,
|
||||
createLoginSource: models.CreateLoginSource,
|
||||
updateLoginSource: models.UpdateSource,
|
||||
getLoginSourceByID: models.GetLoginSourceByID,
|
||||
}
|
||||
}
|
||||
|
||||
// parseLoginSource assigns values on loginSource according to command line flags.
|
||||
func parseLoginSource(c *cli.Context, loginSource *models.LoginSource) {
|
||||
if c.IsSet("name") {
|
||||
loginSource.Name = c.String("name")
|
||||
}
|
||||
if c.IsSet("not-active") {
|
||||
loginSource.IsActived = !c.Bool("not-active")
|
||||
}
|
||||
if c.IsSet("synchronize-users") {
|
||||
loginSource.IsSyncEnabled = c.Bool("synchronize-users")
|
||||
}
|
||||
}
|
||||
|
||||
// parseLdapConfig assigns values on config according to command line flags.
|
||||
func parseLdapConfig(c *cli.Context, config *models.LDAPConfig) error {
|
||||
if c.IsSet("name") {
|
||||
config.Source.Name = c.String("name")
|
||||
}
|
||||
if c.IsSet("host") {
|
||||
config.Source.Host = c.String("host")
|
||||
}
|
||||
if c.IsSet("port") {
|
||||
config.Source.Port = c.Int("port")
|
||||
}
|
||||
if c.IsSet("security-protocol") {
|
||||
p, ok := findLdapSecurityProtocolByName(c.String("security-protocol"))
|
||||
if !ok {
|
||||
return fmt.Errorf("Unknown security protocol name: %s", c.String("security-protocol"))
|
||||
}
|
||||
config.Source.SecurityProtocol = p
|
||||
}
|
||||
if c.IsSet("skip-tls-verify") {
|
||||
config.Source.SkipVerify = c.Bool("skip-tls-verify")
|
||||
}
|
||||
if c.IsSet("bind-dn") {
|
||||
config.Source.BindDN = c.String("bind-dn")
|
||||
}
|
||||
if c.IsSet("user-dn") {
|
||||
config.Source.UserDN = c.String("user-dn")
|
||||
}
|
||||
if c.IsSet("bind-password") {
|
||||
config.Source.BindPassword = c.String("bind-password")
|
||||
}
|
||||
if c.IsSet("user-search-base") {
|
||||
config.Source.UserBase = c.String("user-search-base")
|
||||
}
|
||||
if c.IsSet("username-attribute") {
|
||||
config.Source.AttributeUsername = c.String("username-attribute")
|
||||
}
|
||||
if c.IsSet("firstname-attribute") {
|
||||
config.Source.AttributeName = c.String("firstname-attribute")
|
||||
}
|
||||
if c.IsSet("surname-attribute") {
|
||||
config.Source.AttributeSurname = c.String("surname-attribute")
|
||||
}
|
||||
if c.IsSet("email-attribute") {
|
||||
config.Source.AttributeMail = c.String("email-attribute")
|
||||
}
|
||||
if c.IsSet("attributes-in-bind") {
|
||||
config.Source.AttributesInBind = c.Bool("attributes-in-bind")
|
||||
}
|
||||
if c.IsSet("public-ssh-key-attribute") {
|
||||
config.Source.AttributeSSHPublicKey = c.String("public-ssh-key-attribute")
|
||||
}
|
||||
if c.IsSet("page-size") {
|
||||
config.Source.SearchPageSize = uint32(c.Uint("page-size"))
|
||||
}
|
||||
if c.IsSet("user-filter") {
|
||||
config.Source.Filter = c.String("user-filter")
|
||||
}
|
||||
if c.IsSet("admin-filter") {
|
||||
config.Source.AdminFilter = c.String("admin-filter")
|
||||
}
|
||||
if c.IsSet("restricted-filter") {
|
||||
config.Source.RestrictedFilter = c.String("restricted-filter")
|
||||
}
|
||||
if c.IsSet("allow-deactivate-all") {
|
||||
config.Source.AllowDeactivateAll = c.Bool("allow-deactivate-all")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// findLdapSecurityProtocolByName finds security protocol by its name ignoring case.
|
||||
// It returns the value of the security protocol and if it was found.
|
||||
func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
|
||||
for i, n := range models.SecurityProtocolNames {
|
||||
if strings.EqualFold(name, n) {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// getLoginSource gets the login source by its id defined in the command line flags.
|
||||
// It returns an error if the id is not set, does not match any source or if the source is not of expected type.
|
||||
func (a *authService) getLoginSource(c *cli.Context, loginType models.LoginType) (*models.LoginSource, error) {
|
||||
if err := argsSet(c, "id"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
loginSource, err := a.getLoginSourceByID(c.Int64("id"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if loginSource.Type != loginType {
|
||||
return nil, fmt.Errorf("Invalid authentication type. expected: %s, actual: %s", models.LoginNames[loginType], models.LoginNames[loginSource.Type])
|
||||
}
|
||||
|
||||
return loginSource, nil
|
||||
}
|
||||
|
||||
// addLdapBindDn adds a new LDAP via Bind DN authentication source.
|
||||
func (a *authService) addLdapBindDn(c *cli.Context) error {
|
||||
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-search-base", "user-filter", "email-attribute"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := a.initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loginSource := &models.LoginSource{
|
||||
Type: models.LoginLDAP,
|
||||
IsActived: true, // active by default
|
||||
Cfg: &models.LDAPConfig{
|
||||
Source: &ldap.Source{
|
||||
Enabled: true, // always true
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
parseLoginSource(c, loginSource)
|
||||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.createLoginSource(loginSource)
|
||||
}
|
||||
|
||||
// updateLdapBindDn updates a new LDAP via Bind DN authentication source.
|
||||
func (a *authService) updateLdapBindDn(c *cli.Context) error {
|
||||
if err := a.initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loginSource, err := a.getLoginSource(c, models.LoginLDAP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parseLoginSource(c, loginSource)
|
||||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.updateLoginSource(loginSource)
|
||||
}
|
||||
|
||||
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
|
||||
func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
|
||||
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-dn", "user-filter", "email-attribute"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := a.initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loginSource := &models.LoginSource{
|
||||
Type: models.LoginDLDAP,
|
||||
IsActived: true, // active by default
|
||||
Cfg: &models.LDAPConfig{
|
||||
Source: &ldap.Source{
|
||||
Enabled: true, // always true
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
parseLoginSource(c, loginSource)
|
||||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.createLoginSource(loginSource)
|
||||
}
|
||||
|
||||
// updateLdapBindDn updates a new LDAP (simple auth) authentication source.
|
||||
func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
|
||||
if err := a.initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loginSource, err := a.getLoginSource(c, models.LoginDLDAP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parseLoginSource(c, loginSource)
|
||||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.updateLoginSource(loginSource)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
21
cmd/cert.go
21
cmd/cert.go
@@ -170,28 +170,17 @@ func runCert(c *cli.Context) error {
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open cert.pem for writing: %v", err)
|
||||
}
|
||||
err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to encode certificate: %v", err)
|
||||
}
|
||||
err = certOut.Close()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to write cert: %v", err)
|
||||
}
|
||||
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
certOut.Close()
|
||||
log.Println("Written cert.pem")
|
||||
|
||||
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open key.pem for writing: %v", err)
|
||||
}
|
||||
err = pem.Encode(keyOut, pemBlockForKey(priv))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to encode key: %v", err)
|
||||
}
|
||||
err = keyOut.Close()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to write key: %v", err)
|
||||
}
|
||||
pem.Encode(keyOut, pemBlockForKey(priv))
|
||||
keyOut.Close()
|
||||
log.Println("Written key.pem")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -38,7 +38,7 @@ func initDB() error {
|
||||
|
||||
func initDBDisableConsole(disableConsole bool) error {
|
||||
setting.NewContext()
|
||||
setting.InitDBConfig()
|
||||
models.LoadConfigs()
|
||||
|
||||
setting.NewXORMLogService(disableConsole)
|
||||
if err := models.SetEngine(); err != nil {
|
||||
|
@@ -1,49 +0,0 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// CmdConvert represents the available convert sub-command.
|
||||
var CmdConvert = cli.Command{
|
||||
Name: "convert",
|
||||
Usage: "Convert the database",
|
||||
Description: "A command to convert an existing MySQL database from utf8 to utf8mb4",
|
||||
Action: runConvert,
|
||||
}
|
||||
|
||||
func runConvert(ctx *cli.Context) error {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("AppPath: %s", setting.AppPath)
|
||||
log.Trace("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Trace("Custom path: %s", setting.CustomPath)
|
||||
log.Trace("Log path: %s", setting.LogRootPath)
|
||||
setting.InitDBConfig()
|
||||
|
||||
if !setting.Database.UseMySQL {
|
||||
fmt.Println("This command can only be used with a MySQL database")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := models.ConvertUtf8ToUtf8mb4(); err != nil {
|
||||
log.Fatal("Failed to convert database from utf8 to utf8mb4: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Converted successfully, please confirm your database's character set is now utf8mb4")
|
||||
|
||||
return nil
|
||||
}
|
734
cmd/doctor.go
734
cmd/doctor.go
@@ -1,734 +0,0 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
golog "log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/migrations"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"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/util"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// CmdDoctor represents the available doctor sub-command.
|
||||
var CmdDoctor = cli.Command{
|
||||
Name: "doctor",
|
||||
Usage: "Diagnose problems",
|
||||
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration.",
|
||||
Action: runDoctor,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "list",
|
||||
Usage: "List the available checks",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "default",
|
||||
Usage: "Run the default checks (if neither --run or --all is set, this is the default behaviour)",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "run",
|
||||
Usage: "Run the provided checks - (if --default is set, the default checks will also run)",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "all",
|
||||
Usage: "Run all the available checks",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "fix",
|
||||
Usage: "Automatically fix what we can",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "log-file",
|
||||
Usage: `Name of the log file (default: "doctor.log"). Set to "-" to output to stdout, set to "" to disable`,
|
||||
},
|
||||
},
|
||||
Subcommands: []cli.Command{
|
||||
cmdRecreateTable,
|
||||
},
|
||||
}
|
||||
|
||||
var cmdRecreateTable = cli.Command{
|
||||
Name: "recreate-table",
|
||||
Usage: "Recreate tables from XORM definitions and copy the data.",
|
||||
ArgsUsage: "[TABLE]... : (TABLEs to recreate - leave blank for all)",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
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,
|
||||
}
|
||||
|
||||
type check struct {
|
||||
title string
|
||||
name string
|
||||
isDefault bool
|
||||
f func(ctx *cli.Context) ([]string, error)
|
||||
abortIfFailed bool
|
||||
skipDatabaseInit bool
|
||||
}
|
||||
|
||||
// checklist represents list for all checks
|
||||
var checklist = []check{
|
||||
{
|
||||
// NOTE: this check should be the first in the list
|
||||
title: "Check paths and basic configuration",
|
||||
name: "paths",
|
||||
isDefault: true,
|
||||
f: runDoctorPathInfo,
|
||||
abortIfFailed: true,
|
||||
skipDatabaseInit: true,
|
||||
},
|
||||
{
|
||||
title: "Check Database Version",
|
||||
name: "check-db-version",
|
||||
isDefault: true,
|
||||
f: runDoctorCheckDBVersion,
|
||||
abortIfFailed: false,
|
||||
},
|
||||
{
|
||||
title: "Check consistency of database",
|
||||
name: "check-db-consistency",
|
||||
isDefault: false,
|
||||
f: runDoctorCheckDBConsistency,
|
||||
},
|
||||
{
|
||||
title: "Check if OpenSSH authorized_keys file is up-to-date",
|
||||
name: "authorized_keys",
|
||||
isDefault: true,
|
||||
f: runDoctorAuthorizedKeys,
|
||||
},
|
||||
{
|
||||
title: "Check if SCRIPT_TYPE is available",
|
||||
name: "script-type",
|
||||
isDefault: false,
|
||||
f: runDoctorScriptType,
|
||||
},
|
||||
{
|
||||
title: "Check if hook files are up-to-date and executable",
|
||||
name: "hooks",
|
||||
isDefault: false,
|
||||
f: runDoctorHooks,
|
||||
},
|
||||
{
|
||||
title: "Recalculate merge bases",
|
||||
name: "recalculate_merge_bases",
|
||||
isDefault: false,
|
||||
f: runDoctorPRMergeBase,
|
||||
},
|
||||
{
|
||||
title: "Recalculate Stars number for all user",
|
||||
name: "recalculate_stars_number",
|
||||
isDefault: false,
|
||||
f: runDoctorUserStarNum,
|
||||
},
|
||||
{
|
||||
title: "Enable push options",
|
||||
name: "enable-push-options",
|
||||
isDefault: false,
|
||||
f: runDoctorEnablePushOptions,
|
||||
},
|
||||
// more checks please append here
|
||||
}
|
||||
|
||||
func runRecreateTable(ctx *cli.Context) error {
|
||||
// Redirect the default golog to here
|
||||
golog.SetFlags(0)
|
||||
golog.SetPrefix("")
|
||||
golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT)))
|
||||
|
||||
setting.NewContext()
|
||||
setting.InitDBConfig()
|
||||
|
||||
setting.EnableXORMLog = ctx.Bool("debug")
|
||||
setting.Database.LogSQL = ctx.Bool("debug")
|
||||
setting.Cfg.Section("log").Key("XORM").SetValue(",")
|
||||
|
||||
setting.NewXORMLogService(!ctx.Bool("debug"))
|
||||
if err := models.SetEngine(); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.")
|
||||
return nil
|
||||
}
|
||||
|
||||
args := ctx.Args()
|
||||
names := make([]string, 0, ctx.NArg())
|
||||
for i := 0; i < ctx.NArg(); i++ {
|
||||
names = append(names, args.Get(i))
|
||||
}
|
||||
|
||||
beans, err := models.NamesToBean(names...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
recreateTables := migrations.RecreateTables(beans...)
|
||||
|
||||
return models.NewEngine(context.Background(), func(x *xorm.Engine) error {
|
||||
if err := migrations.EnsureUpToDate(x); err != nil {
|
||||
return err
|
||||
}
|
||||
return recreateTables(x)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func runDoctor(ctx *cli.Context) error {
|
||||
|
||||
// Silence the default loggers
|
||||
log.DelNamedLogger("console")
|
||||
log.DelNamedLogger(log.DEFAULT)
|
||||
|
||||
// Now setup our own
|
||||
logFile := ctx.String("log-file")
|
||||
if !ctx.IsSet("log-file") {
|
||||
logFile = "doctor.log"
|
||||
}
|
||||
|
||||
if len(logFile) == 0 {
|
||||
log.NewLogger(1000, "doctor", "console", `{"level":"NONE","stacktracelevel":"NONE","colorize":"%t"}`)
|
||||
} else if logFile == "-" {
|
||||
log.NewLogger(1000, "doctor", "console", `{"level":"trace","stacktracelevel":"NONE"}`)
|
||||
} else {
|
||||
log.NewLogger(1000, "doctor", "file", fmt.Sprintf(`{"filename":%q,"level":"trace","stacktracelevel":"NONE"}`, logFile))
|
||||
}
|
||||
|
||||
// Finally redirect the default golog to here
|
||||
golog.SetFlags(0)
|
||||
golog.SetPrefix("")
|
||||
golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT)))
|
||||
|
||||
if ctx.IsSet("list") {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 8, 0, '\t', 0)
|
||||
_, _ = w.Write([]byte("Default\tName\tTitle\n"))
|
||||
for _, check := range checklist {
|
||||
if check.isDefault {
|
||||
_, _ = w.Write([]byte{'*'})
|
||||
}
|
||||
_, _ = w.Write([]byte{'\t'})
|
||||
_, _ = w.Write([]byte(check.name))
|
||||
_, _ = w.Write([]byte{'\t'})
|
||||
_, _ = w.Write([]byte(check.title))
|
||||
_, _ = w.Write([]byte{'\n'})
|
||||
}
|
||||
return w.Flush()
|
||||
}
|
||||
|
||||
var checks []check
|
||||
if ctx.Bool("all") {
|
||||
checks = checklist
|
||||
} else if ctx.IsSet("run") {
|
||||
addDefault := ctx.Bool("default")
|
||||
names := ctx.StringSlice("run")
|
||||
for i, name := range names {
|
||||
names[i] = strings.ToLower(strings.TrimSpace(name))
|
||||
}
|
||||
|
||||
for _, check := range checklist {
|
||||
if addDefault && check.isDefault {
|
||||
checks = append(checks, check)
|
||||
continue
|
||||
}
|
||||
for _, name := range names {
|
||||
if name == check.name {
|
||||
checks = append(checks, check)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, check := range checklist {
|
||||
if check.isDefault {
|
||||
checks = append(checks, check)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dbIsInit := false
|
||||
|
||||
for i, check := range checks {
|
||||
if !dbIsInit && !check.skipDatabaseInit {
|
||||
// Only open database after the most basic configuration check
|
||||
setting.EnableXORMLog = false
|
||||
if err := initDBDisableConsole(true); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.")
|
||||
return nil
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
||||
setting.NewContext()
|
||||
|
||||
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 := util.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 runDoctorUserStarNum(ctx *cli.Context) ([]string, error) {
|
||||
return nil, models.DoctorUserStarNum()
|
||||
}
|
||||
|
||||
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 IssueLabels without existing label
|
||||
count, err = models.CountOrphanedIssueLabels()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count > 0 {
|
||||
if ctx.Bool("fix") {
|
||||
if err = models.DeleteOrphanedIssueLabels(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, fmt.Sprintf("%d issue_labels without existing label deleted", count))
|
||||
} else {
|
||||
results = append(results, fmt.Sprintf("%d issue_labels without existing label", 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))
|
||||
}
|
||||
}
|
||||
|
||||
if setting.Database.UsePostgreSQL {
|
||||
count, err = models.CountBadSequences()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count > 0 {
|
||||
if ctx.Bool("fix") {
|
||||
err := models.FixBadSequences()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, fmt.Sprintf("%d sequences updated", count))
|
||||
} else {
|
||||
results = append(results, fmt.Sprintf("%d sequences with incorrect values", count))
|
||||
}
|
||||
}
|
||||
}
|
||||
//ToDo: function to recalc all counters
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func runDoctorEnablePushOptions(ctx *cli.Context) ([]string, error) {
|
||||
numRepos := 0
|
||||
_, err := iterateRepositories(func(repo *models.Repository) ([]string, error) {
|
||||
numRepos++
|
||||
r, err := git.OpenRepository(repo.RepoPath())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
if ctx.Bool("fix") {
|
||||
_, err := git.NewCommand("config", "receive.advertisePushOptions", "true").RunInDir(r.Path)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
var prefix string
|
||||
if !ctx.Bool("fix") {
|
||||
prefix = "DRY RUN: "
|
||||
}
|
||||
return []string{fmt.Sprintf("%sEnabled push options for %d repositories.", prefix, numRepos)}, err
|
||||
}
|
374
cmd/dump.go
374
cmd/dump.go
@@ -6,124 +6,22 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"gitea.com/macaron/session"
|
||||
archiver "github.com/mholt/archiver/v3"
|
||||
"github.com/unknwon/com"
|
||||
"github.com/Unknwon/cae/zip"
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func addFile(w archiver.Writer, filePath string, 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 addRecursive(w archiver.Writer, dirPath string, absPath string, verbose bool) error {
|
||||
if verbose {
|
||||
log.Info("Adding dir %s\n", dirPath)
|
||||
}
|
||||
dir, err := os.Open(absPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not open directory %s: %s", absPath, err)
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
files, err := dir.Readdir(0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to list files in %s: %s", absPath, err)
|
||||
}
|
||||
|
||||
if err := addFile(w, dirPath, absPath, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, fileInfo := range files {
|
||||
if fileInfo.IsDir() {
|
||||
err = addRecursive(w, filepath.Join(dirPath, fileInfo.Name()), filepath.Join(absPath, fileInfo.Name()), verbose)
|
||||
} else {
|
||||
err = addFile(w, filepath.Join(dirPath, fileInfo.Name()), filepath.Join(absPath, fileInfo.Name()), verbose)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isSubdir(upper string, lower string) (bool, error) {
|
||||
if relPath, err := filepath.Rel(upper, lower); err != nil {
|
||||
return false, err
|
||||
} else if relPath == "." || !strings.HasPrefix(relPath, ".") {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
type outputType struct {
|
||||
Enum []string
|
||||
Default string
|
||||
selected string
|
||||
}
|
||||
|
||||
func (o outputType) Join() string {
|
||||
return strings.Join(o.Enum, ", ")
|
||||
}
|
||||
|
||||
func (o *outputType) Set(value string) error {
|
||||
for _, enum := range o.Enum {
|
||||
if enum == value {
|
||||
o.selected = value
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("allowed values are %s", o.Join())
|
||||
}
|
||||
|
||||
func (o outputType) String() string {
|
||||
if o.selected == "" {
|
||||
return o.Default
|
||||
}
|
||||
return o.selected
|
||||
}
|
||||
|
||||
var outputTypeEnum = &outputType{
|
||||
Enum: []string{"zip", "tar", "tar.gz", "tar.xz", "tar.bz2"},
|
||||
Default: "zip",
|
||||
}
|
||||
|
||||
// CmdDump represents the available dump sub-command.
|
||||
var CmdDump = cli.Command{
|
||||
Name: "dump",
|
||||
@@ -133,12 +31,12 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
|
||||
Action: runDump,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "file, f",
|
||||
Value: fmt.Sprintf("gitea-dump-%d.zip", time.Now().Unix()),
|
||||
Usage: "Name of the dump file which will be created. Supply '-' for stdout. See type for available types.",
|
||||
Name: "config, c",
|
||||
Value: "custom/conf/app.ini",
|
||||
Usage: "Custom configuration file path",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "verbose, V",
|
||||
Name: "verbose, v",
|
||||
Usage: "Show process details",
|
||||
},
|
||||
cli.StringFlag{
|
||||
@@ -154,238 +52,120 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
|
||||
Name: "skip-repository, R",
|
||||
Usage: "Skip the repository dumping",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "skip-log, L",
|
||||
Usage: "Skip the log dumping",
|
||||
},
|
||||
cli.GenericFlag{
|
||||
Name: "type",
|
||||
Value: outputTypeEnum,
|
||||
Usage: fmt.Sprintf("Dump output format: %s", outputTypeEnum.Join()),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func fatal(format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, format+"\n", args...)
|
||||
log.Fatal(format, args...)
|
||||
}
|
||||
|
||||
func runDump(ctx *cli.Context) error {
|
||||
var file *os.File
|
||||
fileName := ctx.String("file")
|
||||
if fileName == "-" {
|
||||
file = os.Stdout
|
||||
err := log.DelLogger("console")
|
||||
if err != nil {
|
||||
fatal("Deleting default logger failed. Can not write to stdout: %v", err)
|
||||
}
|
||||
if ctx.IsSet("config") {
|
||||
setting.CustomConf = ctx.String("config")
|
||||
}
|
||||
setting.NewContext()
|
||||
// make sure we are logging to the console no matter what the configuration tells us do to
|
||||
if _, err := setting.Cfg.Section("log").NewKey("MODE", "console"); err != nil {
|
||||
fatal("Setting logging mode to console failed: %v", err)
|
||||
}
|
||||
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
|
||||
models.LoadConfigs()
|
||||
|
||||
err := models.SetEngine()
|
||||
if err != nil {
|
||||
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()
|
||||
|
||||
verbose := ctx.Bool("verbose")
|
||||
outType := ctx.String("type")
|
||||
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 := addRecursive(w, "repos", setting.RepoRootPath, verbose); err != nil {
|
||||
fatal("Failed to include repositories: %v", err)
|
||||
}
|
||||
|
||||
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")
|
||||
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
||||
fatal("Path does not exist: %s", tmpDir)
|
||||
log.Fatalf("Path does not exist: %s", tmpDir)
|
||||
}
|
||||
tmpWorkDir, err := ioutil.TempDir(tmpDir, "gitea-dump-")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create tmp work directory: %v", err)
|
||||
}
|
||||
log.Printf("Creating tmp work dir: %s", tmpWorkDir)
|
||||
|
||||
// work-around #1103
|
||||
if os.Getenv("TMPDIR") == "" {
|
||||
os.Setenv("TMPDIR", tmpWorkDir)
|
||||
}
|
||||
|
||||
dbDump, err := ioutil.TempFile(tmpDir, "gitea-db.sql")
|
||||
dbDump := path.Join(tmpWorkDir, "gitea-db.sql")
|
||||
|
||||
fileName := fmt.Sprintf("gitea-dump-%d.zip", time.Now().Unix())
|
||||
log.Printf("Packing dump files...")
|
||||
z, err := zip.Create(fileName)
|
||||
if err != nil {
|
||||
fatal("Failed to create tmp file: %v", err)
|
||||
log.Fatalf("Failed to create %s: %v", fileName, err)
|
||||
}
|
||||
defer func() {
|
||||
if err := util.Remove(dbDump.Name()); err != nil {
|
||||
log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err)
|
||||
zip.Verbose = ctx.Bool("verbose")
|
||||
|
||||
if ctx.IsSet("skip-repository") {
|
||||
log.Printf("Skip dumping local repositories")
|
||||
} else {
|
||||
log.Printf("Dumping local repositories...%s", setting.RepoRootPath)
|
||||
reposDump := path.Join(tmpWorkDir, "gitea-repo.zip")
|
||||
if err := zip.PackTo(setting.RepoRootPath, reposDump, true); err != nil {
|
||||
log.Fatalf("Failed to dump local repositories: %v", err)
|
||||
}
|
||||
}()
|
||||
if err := z.AddFile("gitea-repo.zip", reposDump); err != nil {
|
||||
log.Fatalf("Failed to include gitea-repo.zip: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
targetDBType := ctx.String("database")
|
||||
if len(targetDBType) > 0 && targetDBType != setting.Database.Type {
|
||||
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
|
||||
if len(targetDBType) > 0 && targetDBType != models.DbCfg.Type {
|
||||
log.Printf("Dumping database %s => %s...", models.DbCfg.Type, targetDBType)
|
||||
} else {
|
||||
log.Info("Dumping database...")
|
||||
log.Printf("Dumping database...")
|
||||
}
|
||||
|
||||
if err := models.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
|
||||
fatal("Failed to dump database: %v", err)
|
||||
if err := models.DumpDatabase(dbDump, targetDBType); err != nil {
|
||||
log.Fatalf("Failed to dump database: %v", err)
|
||||
}
|
||||
|
||||
if err := addFile(w, "gitea-db.sql", dbDump.Name(), verbose); err != nil {
|
||||
fatal("Failed to include gitea-db.sql: %v", err)
|
||||
if err := z.AddFile("gitea-db.sql", dbDump); err != nil {
|
||||
log.Fatalf("Failed to include gitea-db.sql: %v", err)
|
||||
}
|
||||
|
||||
if len(setting.CustomConf) > 0 {
|
||||
log.Info("Adding custom configuration file from %s", setting.CustomConf)
|
||||
if err := addFile(w, "app.ini", setting.CustomConf, verbose); err != nil {
|
||||
fatal("Failed to include specified app.ini: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
customDir, err := os.Stat(setting.CustomPath)
|
||||
if err == nil && customDir.IsDir() {
|
||||
if is, _ := isSubdir(setting.AppDataPath, setting.CustomPath); !is {
|
||||
if err := addRecursive(w, "custom", setting.CustomPath, 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)
|
||||
if err := z.AddDir("custom", setting.CustomPath); err != nil {
|
||||
log.Fatalf("Failed to include custom: %v", err)
|
||||
}
|
||||
} else {
|
||||
log.Info("Custom dir %s doesn't exist, skipped", setting.CustomPath)
|
||||
log.Printf("Custom dir %s doesn't exist, skipped", setting.CustomPath)
|
||||
}
|
||||
|
||||
if com.IsExist(setting.AppDataPath) {
|
||||
log.Info("Packing data directory...%s", setting.AppDataPath)
|
||||
log.Printf("Packing data directory...%s", setting.AppDataPath)
|
||||
|
||||
var excludes []string
|
||||
if setting.Cfg.Section("session").Key("PROVIDER").Value() == "file" {
|
||||
var opts session.Options
|
||||
if err = json.Unmarshal([]byte(setting.SessionConfig.ProviderConfig), &opts); err != nil {
|
||||
return err
|
||||
}
|
||||
excludes = append(excludes, opts.ProviderConfig)
|
||||
var sessionAbsPath string
|
||||
if setting.SessionConfig.Provider == "file" {
|
||||
sessionAbsPath = setting.SessionConfig.ProviderConfig
|
||||
}
|
||||
|
||||
excludes = append(excludes, setting.RepoRootPath)
|
||||
excludes = append(excludes, setting.LFS.Path)
|
||||
excludes = append(excludes, setting.Attachment.Path)
|
||||
excludes = append(excludes, setting.LogRootPath)
|
||||
if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil {
|
||||
fatal("Failed to include data directory: %v", err)
|
||||
if err := zipAddDirectoryExclude(z, "data", setting.AppDataPath, sessionAbsPath); err != nil {
|
||||
log.Fatalf("Failed to include data directory: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
if err := z.AddDir("log", setting.LogRootPath); err != nil {
|
||||
log.Fatalf("Failed to include log: %v", err)
|
||||
}
|
||||
|
||||
// Doesn't check if LogRootPath exists before processing --skip-log intentionally,
|
||||
// ensuring that it's clear the dump is skipped whether the directory's initialized
|
||||
// yet or not.
|
||||
if ctx.IsSet("skip-log") && ctx.Bool("skip-log") {
|
||||
log.Info("Skip dumping log files")
|
||||
} else if com.IsExist(setting.LogRootPath) {
|
||||
if err := addRecursive(w, "log", setting.LogRootPath, verbose); err != nil {
|
||||
fatal("Failed to include log: %v", err)
|
||||
}
|
||||
if err = z.Close(); err != nil {
|
||||
_ = os.Remove(fileName)
|
||||
log.Fatalf("Failed to save %s: %v", fileName, err)
|
||||
}
|
||||
|
||||
if fileName != "-" {
|
||||
if err = w.Close(); err != nil {
|
||||
_ = util.Remove(fileName)
|
||||
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 err := os.Chmod(fileName, 0600); err != nil {
|
||||
log.Printf("Can't change file access permissions mask to 0600: %v", err)
|
||||
}
|
||||
|
||||
if fileName != "-" {
|
||||
log.Info("Finish dumping in file %s", fileName)
|
||||
} else {
|
||||
log.Info("Finish dumping to stdout")
|
||||
log.Printf("Removing tmp work dir: %s", tmpWorkDir)
|
||||
|
||||
if err := os.RemoveAll(tmpWorkDir); err != nil {
|
||||
log.Fatalf("Failed to remove %s: %v", tmpWorkDir, err)
|
||||
}
|
||||
log.Printf("Finish dumping in file %s", fileName)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func contains(slice []string, s string) bool {
|
||||
for _, v := range slice {
|
||||
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 {
|
||||
// zipAddDirectoryExclude zips absPath to specified zipPath inside z excluding excludeAbsPath
|
||||
func zipAddDirectoryExclude(zip *zip.ZipArchive, zipPath, absPath string, excludeAbsPath string) error {
|
||||
absPath, err := filepath.Abs(absPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -396,24 +176,24 @@ func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeA
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
zip.AddEmptyDir(zipPath)
|
||||
|
||||
files, err := dir.Readdir(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, file := range files {
|
||||
currentAbsPath := path.Join(absPath, file.Name())
|
||||
currentInsidePath := path.Join(insidePath, file.Name())
|
||||
currentZipPath := path.Join(zipPath, file.Name())
|
||||
if file.IsDir() {
|
||||
if !contains(excludeAbsPath, currentAbsPath) {
|
||||
if err := addFile(w, currentInsidePath, currentAbsPath, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = addRecursiveExclude(w, currentInsidePath, currentAbsPath, excludeAbsPath, verbose); err != nil {
|
||||
if currentAbsPath != excludeAbsPath {
|
||||
if err = zipAddDirectoryExclude(zip, currentZipPath, currentAbsPath, excludeAbsPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if err = addFile(w, currentInsidePath, currentAbsPath, verbose); err != nil {
|
||||
if err = zip.AddFile(currentZipPath, currentAbsPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
332
cmd/embedded.go
332
cmd/embedded.go
@@ -1,332 +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.
|
||||
|
||||
// +build bindata
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/options"
|
||||
"code.gitea.io/gitea/modules/public"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
|
||||
"github.com/gobwas/glob"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// Cmdembedded represents the available extract sub-command.
|
||||
var (
|
||||
Cmdembedded = cli.Command{
|
||||
Name: "embedded",
|
||||
Usage: "Extract embedded resources",
|
||||
Description: "A command for extracting embedded resources, like templates and images",
|
||||
Subcommands: []cli.Command{
|
||||
subcmdList,
|
||||
subcmdView,
|
||||
subcmdExtract,
|
||||
},
|
||||
}
|
||||
|
||||
subcmdList = cli.Command{
|
||||
Name: "list",
|
||||
Usage: "List files matching the given pattern",
|
||||
Action: runList,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "include-vendored,vendor",
|
||||
Usage: "Include files under public/vendor as well",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
subcmdView = cli.Command{
|
||||
Name: "view",
|
||||
Usage: "View a file matching the given pattern",
|
||||
Action: runView,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "include-vendored,vendor",
|
||||
Usage: "Include files under public/vendor as well",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
subcmdExtract = cli.Command{
|
||||
Name: "extract",
|
||||
Usage: "Extract resources",
|
||||
Action: runExtract,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "include-vendored,vendor",
|
||||
Usage: "Include files under public/vendor as well",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "overwrite",
|
||||
Usage: "Overwrite files if they already exist",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "rename",
|
||||
Usage: "Rename files as {name}.bak if they already exist (overwrites previous .bak)",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "custom",
|
||||
Usage: "Extract to the 'custom' directory as per app.ini",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "destination,dest-dir",
|
||||
Usage: "Extract to the specified directory",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
sections map[string]*section
|
||||
assets []asset
|
||||
)
|
||||
|
||||
type section struct {
|
||||
Path string
|
||||
Names func() []string
|
||||
IsDir func(string) (bool, error)
|
||||
Asset func(string) ([]byte, error)
|
||||
}
|
||||
|
||||
type asset struct {
|
||||
Section *section
|
||||
Name string
|
||||
Path string
|
||||
}
|
||||
|
||||
func initEmbeddedExtractor(c *cli.Context) error {
|
||||
|
||||
// Silence the console logger
|
||||
log.DelNamedLogger("console")
|
||||
log.DelNamedLogger(log.DEFAULT)
|
||||
|
||||
// Read configuration file
|
||||
setting.NewContext()
|
||||
|
||||
pats, err := getPatterns(c.Args())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sections := make(map[string]*section, 3)
|
||||
|
||||
sections["public"] = §ion{Path: "public", Names: public.AssetNames, IsDir: public.AssetIsDir, Asset: public.Asset}
|
||||
sections["options"] = §ion{Path: "options", Names: options.AssetNames, IsDir: options.AssetIsDir, Asset: options.Asset}
|
||||
sections["templates"] = §ion{Path: "templates", Names: templates.AssetNames, IsDir: templates.AssetIsDir, Asset: templates.Asset}
|
||||
|
||||
for _, sec := range sections {
|
||||
assets = append(assets, buildAssetList(sec, pats, c)...)
|
||||
}
|
||||
|
||||
// Sort assets
|
||||
sort.SliceStable(assets, func(i, j int) bool {
|
||||
return assets[i].Path < assets[j].Path
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runList(c *cli.Context) error {
|
||||
if err := runListDo(c); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runView(c *cli.Context) error {
|
||||
if err := runViewDo(c); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runExtract(c *cli.Context) error {
|
||||
if err := runExtractDo(c); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runListDo(c *cli.Context) error {
|
||||
if err := initEmbeddedExtractor(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, a := range assets {
|
||||
fmt.Println(a.Path)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runViewDo(c *cli.Context) error {
|
||||
if err := initEmbeddedExtractor(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(assets) == 0 {
|
||||
return fmt.Errorf("No files matched the given pattern")
|
||||
} else if len(assets) > 1 {
|
||||
return fmt.Errorf("Too many files matched the given pattern; try to be more specific")
|
||||
}
|
||||
|
||||
data, err := assets[0].Section.Asset(assets[0].Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %v", assets[0].Path, err)
|
||||
}
|
||||
|
||||
if _, err = os.Stdout.Write(data); err != nil {
|
||||
return fmt.Errorf("%s: %v", assets[0].Path, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runExtractDo(c *cli.Context) error {
|
||||
if err := initEmbeddedExtractor(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(c.Args()) == 0 {
|
||||
return fmt.Errorf("A list of pattern of files to extract is mandatory (e.g. '**' for all)")
|
||||
}
|
||||
|
||||
destdir := "."
|
||||
|
||||
if c.IsSet("destination") {
|
||||
destdir = c.String("destination")
|
||||
} else if c.Bool("custom") {
|
||||
destdir = setting.CustomPath
|
||||
fmt.Println("Using app.ini at", setting.CustomConf)
|
||||
}
|
||||
|
||||
fi, err := os.Stat(destdir)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// In case Windows users attempt to provide a forward-slash path
|
||||
wdestdir := filepath.FromSlash(destdir)
|
||||
if wfi, werr := os.Stat(wdestdir); werr == nil {
|
||||
destdir = wdestdir
|
||||
fi = wfi
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %s", destdir, err)
|
||||
} else if !fi.IsDir() {
|
||||
return fmt.Errorf("%s is not a directory.", destdir)
|
||||
}
|
||||
|
||||
fmt.Printf("Extracting to %s:\n", destdir)
|
||||
|
||||
overwrite := c.Bool("overwrite")
|
||||
rename := c.Bool("rename")
|
||||
|
||||
for _, a := range assets {
|
||||
if err := extractAsset(destdir, a, overwrite, rename); err != nil {
|
||||
// Non-fatal error
|
||||
fmt.Fprintf(os.Stderr, "%s: %v", a.Path, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractAsset(d string, a asset, overwrite, rename bool) error {
|
||||
dest := filepath.Join(d, filepath.FromSlash(a.Path))
|
||||
dir := filepath.Dir(dest)
|
||||
|
||||
data, err := a.Section.Asset(a.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %v", a.Path, err)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("%s: %v", dir, err)
|
||||
}
|
||||
|
||||
perms := os.ModePerm & 0666
|
||||
|
||||
fi, err := os.Lstat(dest)
|
||||
if err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
return fmt.Errorf("%s: %v", dest, err)
|
||||
}
|
||||
} else if !overwrite && !rename {
|
||||
fmt.Printf("%s already exists; skipped.\n", dest)
|
||||
return nil
|
||||
} else if !fi.Mode().IsRegular() {
|
||||
return fmt.Errorf("%s already exists, but it's not a regular file", dest)
|
||||
} else if rename {
|
||||
if err := os.Rename(dest, dest+".bak"); err != nil {
|
||||
return fmt.Errorf("Error creating backup for %s: %v", dest, err)
|
||||
}
|
||||
// Attempt to respect file permissions mask (even if user:group will be set anew)
|
||||
perms = fi.Mode()
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(dest, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, perms)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %v", dest, err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if _, err = file.Write(data); err != nil {
|
||||
return fmt.Errorf("%s: %v", dest, err)
|
||||
}
|
||||
|
||||
fmt.Println(dest)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildAssetList(sec *section, globs []glob.Glob, c *cli.Context) []asset {
|
||||
var results = make([]asset, 0, 64)
|
||||
for _, name := range sec.Names() {
|
||||
if isdir, err := sec.IsDir(name); !isdir && err == nil {
|
||||
if sec.Path == "public" &&
|
||||
strings.HasPrefix(name, "vendor/") &&
|
||||
!c.Bool("include-vendored") {
|
||||
continue
|
||||
}
|
||||
matchName := sec.Path + "/" + name
|
||||
for _, g := range globs {
|
||||
if g.Match(matchName) {
|
||||
results = append(results, asset{Section: sec,
|
||||
Name: name,
|
||||
Path: sec.Path + "/" + name})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func getPatterns(args []string) ([]glob.Glob, error) {
|
||||
if len(args) == 0 {
|
||||
args = []string{"**"}
|
||||
}
|
||||
pat := make([]glob.Glob, len(args))
|
||||
for i := range args {
|
||||
if g, err := glob.Compile(args[i], '/'); err != nil {
|
||||
return nil, fmt.Errorf("'%s': Invalid glob pattern: %v", args[i], err)
|
||||
} else {
|
||||
pat[i] = g
|
||||
}
|
||||
}
|
||||
return pat, nil
|
||||
}
|
@@ -1,30 +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.
|
||||
|
||||
// +build !bindata
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// Cmdembedded represents the available extract sub-command.
|
||||
var (
|
||||
Cmdembedded = cli.Command{
|
||||
Name: "embedded",
|
||||
Usage: "Extract embedded resources",
|
||||
Description: "A command for extracting embedded resources, like templates and images",
|
||||
Action: extractorNotImplemented,
|
||||
}
|
||||
)
|
||||
|
||||
func extractorNotImplemented(c *cli.Context) error {
|
||||
err := fmt.Errorf("Sorry: the 'embedded' subcommand is not available in builds without bindata")
|
||||
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||
return err
|
||||
}
|
@@ -7,11 +7,9 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"code.gitea.io/gitea/modules/generate"
|
||||
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
@@ -42,10 +40,9 @@ var (
|
||||
}
|
||||
|
||||
microcmdGenerateLfsJwtSecret = cli.Command{
|
||||
Name: "JWT_SECRET",
|
||||
Aliases: []string{"LFS_JWT_SECRET"},
|
||||
Usage: "Generate a new JWT_SECRET",
|
||||
Action: runGenerateLfsJwtSecret,
|
||||
Name: "LFS_JWT_SECRET",
|
||||
Usage: "Generate a new LFS_JWT_SECRET",
|
||||
Action: runGenerateLfsJwtSecret,
|
||||
}
|
||||
|
||||
microcmdGenerateSecretKey = cli.Command{
|
||||
@@ -61,12 +58,7 @@ func runGenerateInternalToken(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%s", internalToken)
|
||||
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", internalToken)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -76,12 +68,7 @@ func runGenerateLfsJwtSecret(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%s", JWTSecretBase64)
|
||||
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", JWTSecretBase64)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -91,11 +78,6 @@ func runGenerateSecretKey(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%s", secretKey)
|
||||
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", secretKey)
|
||||
return nil
|
||||
}
|
||||
|
453
cmd/hook.go
453
cmd/hook.go
@@ -8,15 +8,13 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/git"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
@@ -24,16 +22,19 @@ import (
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
const (
|
||||
hookBatchSize = 30
|
||||
)
|
||||
|
||||
var (
|
||||
// CmdHook represents the available hooks sub-command.
|
||||
CmdHook = cli.Command{
|
||||
Name: "hook",
|
||||
Usage: "Delegate commands to corresponding Git hooks",
|
||||
Description: "This should only be called by Git",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "config, c",
|
||||
Value: "custom/conf/app.ini",
|
||||
Usage: "Custom configuration file path",
|
||||
},
|
||||
},
|
||||
Subcommands: []cli.Command{
|
||||
subcmdHookPreReceive,
|
||||
subcmdHookUpdate,
|
||||
@@ -46,165 +47,48 @@ var (
|
||||
Usage: "Delegate pre-receive Git hook",
|
||||
Description: "This command should only be called by Git",
|
||||
Action: runHookPreReceive,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
}
|
||||
subcmdHookUpdate = cli.Command{
|
||||
Name: "update",
|
||||
Usage: "Delegate update Git hook",
|
||||
Description: "This command should only be called by Git",
|
||||
Action: runHookUpdate,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
}
|
||||
subcmdHookPostReceive = cli.Command{
|
||||
Name: "post-receive",
|
||||
Usage: "Delegate post-receive Git hook",
|
||||
Description: "This command should only be called by Git",
|
||||
Action: runHookPostReceive,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type delayWriter struct {
|
||||
internal io.Writer
|
||||
buf *bytes.Buffer
|
||||
timer *time.Timer
|
||||
}
|
||||
|
||||
func newDelayWriter(internal io.Writer, delay time.Duration) *delayWriter {
|
||||
timer := time.NewTimer(delay)
|
||||
return &delayWriter{
|
||||
internal: internal,
|
||||
buf: &bytes.Buffer{},
|
||||
timer: timer,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *delayWriter) Write(p []byte) (n int, err error) {
|
||||
if d.buf != nil {
|
||||
select {
|
||||
case <-d.timer.C:
|
||||
_, err := d.internal.Write(d.buf.Bytes())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d.buf = nil
|
||||
return d.internal.Write(p)
|
||||
default:
|
||||
return d.buf.Write(p)
|
||||
}
|
||||
}
|
||||
return d.internal.Write(p)
|
||||
}
|
||||
|
||||
func (d *delayWriter) WriteString(s string) (n int, err error) {
|
||||
if d.buf != nil {
|
||||
select {
|
||||
case <-d.timer.C:
|
||||
_, err := d.internal.Write(d.buf.Bytes())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
d.buf = nil
|
||||
return d.internal.Write([]byte(s))
|
||||
default:
|
||||
return d.buf.WriteString(s)
|
||||
}
|
||||
}
|
||||
return d.internal.Write([]byte(s))
|
||||
}
|
||||
|
||||
func (d *delayWriter) Close() error {
|
||||
if d == nil {
|
||||
return nil
|
||||
}
|
||||
stopped := util.StopTimer(d.timer)
|
||||
if stopped || d.buf == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := d.internal.Write(d.buf.Bytes())
|
||||
d.buf = nil
|
||||
return err
|
||||
}
|
||||
|
||||
type nilWriter struct{}
|
||||
|
||||
func (n *nilWriter) Write(p []byte) (int, error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (n *nilWriter) WriteString(s string) (int, error) {
|
||||
return len(s), nil
|
||||
}
|
||||
|
||||
func runHookPreReceive(c *cli.Context) error {
|
||||
if os.Getenv(models.EnvIsInternal) == "true" {
|
||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
setup("hooks/pre-receive.log", c.Bool("debug"))
|
||||
|
||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
||||
fail(`Rejecting changes as Gitea environment not set.
|
||||
If you are pushing over SSH you must push with a key managed by
|
||||
Gitea or set your environment appropriately.`, "")
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
} else if c.GlobalIsSet("config") {
|
||||
setting.CustomConf = c.GlobalString("config")
|
||||
}
|
||||
|
||||
setup("hooks/pre-receive.log")
|
||||
|
||||
// the environment setted on serv command
|
||||
repoID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchRepoID), 10, 64)
|
||||
isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
|
||||
username := os.Getenv(models.EnvRepoUsername)
|
||||
reponame := os.Getenv(models.EnvRepoName)
|
||||
userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||
prID, _ := strconv.ParseInt(os.Getenv(models.EnvPRID), 10, 64)
|
||||
isDeployKey, _ := strconv.ParseBool(os.Getenv(models.EnvIsDeployKey))
|
||||
|
||||
hookOptions := private.HookOptions{
|
||||
UserID: userID,
|
||||
GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
|
||||
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
||||
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
||||
GitPushOptions: pushOptions(),
|
||||
ProtectedBranchID: prID,
|
||||
IsDeployKey: isDeployKey,
|
||||
}
|
||||
userIDStr := os.Getenv(models.EnvPusherID)
|
||||
repoPath := models.RepoPath(username, reponame)
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
|
||||
oldCommitIDs := make([]string, hookBatchSize)
|
||||
newCommitIDs := make([]string, hookBatchSize)
|
||||
refFullNames := make([]string, hookBatchSize)
|
||||
count := 0
|
||||
total := 0
|
||||
lastline := 0
|
||||
|
||||
var out io.Writer
|
||||
out = &nilWriter{}
|
||||
if setting.Git.VerbosePush {
|
||||
if setting.Git.VerbosePushDelay > 0 {
|
||||
dWriter := newDelayWriter(os.Stdout, setting.Git.VerbosePushDelay)
|
||||
defer dWriter.Close()
|
||||
out = dWriter
|
||||
} else {
|
||||
out = os.Stdout
|
||||
}
|
||||
}
|
||||
|
||||
for scanner.Scan() {
|
||||
buf.Write(scanner.Bytes())
|
||||
buf.WriteByte('\n')
|
||||
|
||||
// TODO: support news feeds for wiki
|
||||
if isWiki {
|
||||
continue
|
||||
@@ -218,134 +102,85 @@ Gitea or set your environment appropriately.`, "")
|
||||
oldCommitID := string(fields[0])
|
||||
newCommitID := string(fields[1])
|
||||
refFullName := string(fields[2])
|
||||
total++
|
||||
lastline++
|
||||
|
||||
// If the ref is a branch, check if it's protected
|
||||
if strings.HasPrefix(refFullName, git.BranchPrefix) {
|
||||
oldCommitIDs[count] = oldCommitID
|
||||
newCommitIDs[count] = newCommitID
|
||||
refFullNames[count] = refFullName
|
||||
count++
|
||||
fmt.Fprintf(out, "*")
|
||||
branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
|
||||
protectBranch, err := private.GetProtectedBranchBy(repoID, branchName)
|
||||
if err != nil {
|
||||
fail("Internal error", fmt.Sprintf("retrieve protected branches information failed: %v", err))
|
||||
}
|
||||
|
||||
if count >= hookBatchSize {
|
||||
fmt.Fprintf(out, " Checking %d branches\n", count)
|
||||
|
||||
hookOptions.OldCommitIDs = oldCommitIDs
|
||||
hookOptions.NewCommitIDs = newCommitIDs
|
||||
hookOptions.RefFullNames = refFullNames
|
||||
statusCode, msg := private.HookPreReceive(username, reponame, hookOptions)
|
||||
switch statusCode {
|
||||
case http.StatusOK:
|
||||
// no-op
|
||||
case http.StatusInternalServerError:
|
||||
fail("Internal Server Error", msg)
|
||||
default:
|
||||
fail(msg, "")
|
||||
}
|
||||
count = 0
|
||||
lastline = 0
|
||||
if protectBranch != nil && protectBranch.IsProtected() {
|
||||
// check and deletion
|
||||
if newCommitID == git.EmptySHA {
|
||||
fail(fmt.Sprintf("branch %s is protected from deletion", branchName), "")
|
||||
}
|
||||
|
||||
// detect force push
|
||||
if git.EmptySHA != oldCommitID {
|
||||
output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+newCommitID).RunInDir(repoPath)
|
||||
if err != nil {
|
||||
fail("Internal error", "Fail to detect force push: %v", err)
|
||||
} else if len(output) > 0 {
|
||||
fail(fmt.Sprintf("branch %s is protected from force push", branchName), "")
|
||||
}
|
||||
}
|
||||
|
||||
userID, _ := strconv.ParseInt(userIDStr, 10, 64)
|
||||
canPush, err := private.CanUserPush(protectBranch.ID, userID)
|
||||
if err != nil {
|
||||
fail("Internal error", "Fail to detect user can push: %v", err)
|
||||
} else if !canPush {
|
||||
fail(fmt.Sprintf("protected branch %s can not be pushed to", branchName), "")
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(out, ".")
|
||||
}
|
||||
if lastline >= hookBatchSize {
|
||||
fmt.Fprintf(out, "\n")
|
||||
lastline = 0
|
||||
}
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
hookOptions.OldCommitIDs = oldCommitIDs[:count]
|
||||
hookOptions.NewCommitIDs = newCommitIDs[:count]
|
||||
hookOptions.RefFullNames = refFullNames[:count]
|
||||
|
||||
fmt.Fprintf(out, " Checking %d branches\n", count)
|
||||
|
||||
statusCode, msg := private.HookPreReceive(username, reponame, hookOptions)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
fail("Internal Server Error", msg)
|
||||
case http.StatusForbidden:
|
||||
fail(msg, "")
|
||||
}
|
||||
} else if lastline > 0 {
|
||||
fmt.Fprintf(out, "\n")
|
||||
lastline = 0
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "Checked %d references in total\n", total)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runHookUpdate(c *cli.Context) error {
|
||||
// Update is empty and is kept only for backwards compatibility
|
||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
} else if c.GlobalIsSet("config") {
|
||||
setting.CustomConf = c.GlobalString("config")
|
||||
}
|
||||
|
||||
setup("hooks/update.log")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runHookPostReceive(c *cli.Context) error {
|
||||
// First of all run update-server-info no matter what
|
||||
if _, err := git.NewCommand("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 len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
setup("hooks/post-receive.log", c.Bool("debug"))
|
||||
|
||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
||||
fail(`Rejecting changes as Gitea environment not set.
|
||||
If you are pushing over SSH you must push with a key managed by
|
||||
Gitea or set your environment appropriately.`, "")
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
} else if c.GlobalIsSet("config") {
|
||||
setting.CustomConf = c.GlobalString("config")
|
||||
}
|
||||
|
||||
var out io.Writer
|
||||
var dWriter *delayWriter
|
||||
out = &nilWriter{}
|
||||
if setting.Git.VerbosePush {
|
||||
if setting.Git.VerbosePushDelay > 0 {
|
||||
dWriter = newDelayWriter(os.Stdout, setting.Git.VerbosePushDelay)
|
||||
defer dWriter.Close()
|
||||
out = dWriter
|
||||
} else {
|
||||
out = os.Stdout
|
||||
}
|
||||
}
|
||||
setup("hooks/post-receive.log")
|
||||
|
||||
// the environment setted on serv command
|
||||
repoID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchRepoID), 10, 64)
|
||||
repoUser := os.Getenv(models.EnvRepoUsername)
|
||||
isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
|
||||
repoName := os.Getenv(models.EnvRepoName)
|
||||
pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||
pusherName := os.Getenv(models.EnvPusherName)
|
||||
|
||||
hookOptions := private.HookOptions{
|
||||
UserName: pusherName,
|
||||
UserID: pusherID,
|
||||
GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
|
||||
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
||||
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
||||
GitPushOptions: pushOptions(),
|
||||
}
|
||||
oldCommitIDs := make([]string, hookBatchSize)
|
||||
newCommitIDs := make([]string, hookBatchSize)
|
||||
refFullNames := make([]string, hookBatchSize)
|
||||
count := 0
|
||||
total := 0
|
||||
wasEmpty := false
|
||||
masterPushed := false
|
||||
results := make([]private.HookPostReceiveBranchResult, 0)
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
buf.Write(scanner.Bytes())
|
||||
buf.WriteByte('\n')
|
||||
|
||||
// TODO: support news feeds for wiki
|
||||
if isWiki {
|
||||
continue
|
||||
@@ -356,107 +191,63 @@ Gitea or set your environment appropriately.`, "")
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, ".")
|
||||
oldCommitIDs[count] = string(fields[0])
|
||||
newCommitIDs[count] = string(fields[1])
|
||||
refFullNames[count] = string(fields[2])
|
||||
if refFullNames[count] == git.BranchPrefix+"master" && newCommitIDs[count] != git.EmptySHA && count == total {
|
||||
masterPushed = true
|
||||
}
|
||||
count++
|
||||
total++
|
||||
oldCommitID := string(fields[0])
|
||||
newCommitID := string(fields[1])
|
||||
refFullName := string(fields[2])
|
||||
|
||||
if count >= hookBatchSize {
|
||||
fmt.Fprintf(out, " Processing %d references\n", count)
|
||||
hookOptions.OldCommitIDs = oldCommitIDs
|
||||
hookOptions.NewCommitIDs = newCommitIDs
|
||||
hookOptions.RefFullNames = refFullNames
|
||||
resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
|
||||
if resp == nil {
|
||||
_ = dWriter.Close()
|
||||
hookPrintResults(results)
|
||||
fail("Internal Server Error", err)
|
||||
}
|
||||
wasEmpty = wasEmpty || resp.RepoWasEmpty
|
||||
results = append(results, resp.Results...)
|
||||
count = 0
|
||||
if err := private.PushUpdate(models.PushUpdateOptions{
|
||||
RefFullName: refFullName,
|
||||
OldCommitID: oldCommitID,
|
||||
NewCommitID: newCommitID,
|
||||
PusherID: pusherID,
|
||||
PusherName: pusherName,
|
||||
RepoUserName: repoUser,
|
||||
RepoName: repoName,
|
||||
}); err != nil {
|
||||
log.GitLogger.Error(2, "Update: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
if wasEmpty && masterPushed {
|
||||
// We need to tell the repo to reset the default branch to master
|
||||
err := private.SetDefaultBranch(repoUser, repoName, "master")
|
||||
if newCommitID != git.EmptySHA && strings.HasPrefix(refFullName, git.BranchPrefix) {
|
||||
branch := strings.TrimPrefix(refFullName, git.BranchPrefix)
|
||||
repo, pullRequestAllowed, err := private.GetRepository(repoID)
|
||||
if err != nil {
|
||||
fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
|
||||
log.GitLogger.Error(2, "get repo: %v", err)
|
||||
break
|
||||
}
|
||||
if !pullRequestAllowed {
|
||||
break
|
||||
}
|
||||
|
||||
baseRepo := repo
|
||||
if repo.IsFork {
|
||||
baseRepo = repo.BaseRepo
|
||||
}
|
||||
|
||||
if !repo.IsFork && branch == baseRepo.DefaultBranch {
|
||||
break
|
||||
}
|
||||
|
||||
pr, err := private.ActivePullRequest(baseRepo.ID, repo.ID, baseRepo.DefaultBranch, branch)
|
||||
if err != nil {
|
||||
log.GitLogger.Error(2, "get active pr: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
if pr == nil {
|
||||
if repo.IsFork {
|
||||
branch = fmt.Sprintf("%s:%s", repo.OwnerName, branch)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", branch)
|
||||
fmt.Fprintf(os.Stderr, " %s/compare/%s...%s\n", baseRepo.HTMLURL(), util.PathEscapeSegments(baseRepo.DefaultBranch), util.PathEscapeSegments(branch))
|
||||
} else {
|
||||
fmt.Fprint(os.Stderr, "Visit the existing pull request:\n")
|
||||
fmt.Fprintf(os.Stderr, " %s/pulls/%d\n", baseRepo.HTMLURL(), pr.Index)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
}
|
||||
fmt.Fprintf(out, "Processed %d references in total\n", total)
|
||||
|
||||
_ = dWriter.Close()
|
||||
hookPrintResults(results)
|
||||
return nil
|
||||
}
|
||||
|
||||
hookOptions.OldCommitIDs = oldCommitIDs[:count]
|
||||
hookOptions.NewCommitIDs = newCommitIDs[:count]
|
||||
hookOptions.RefFullNames = refFullNames[:count]
|
||||
|
||||
fmt.Fprintf(out, " Processing %d references\n", count)
|
||||
|
||||
resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
|
||||
if resp == nil {
|
||||
_ = dWriter.Close()
|
||||
hookPrintResults(results)
|
||||
fail("Internal Server Error", err)
|
||||
}
|
||||
wasEmpty = wasEmpty || resp.RepoWasEmpty
|
||||
results = append(results, resp.Results...)
|
||||
|
||||
fmt.Fprintf(out, "Processed %d references in total\n", total)
|
||||
|
||||
if wasEmpty && masterPushed {
|
||||
// We need to tell the repo to reset the default branch to master
|
||||
err := private.SetDefaultBranch(repoUser, repoName, "master")
|
||||
if err != nil {
|
||||
fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
|
||||
}
|
||||
}
|
||||
_ = dWriter.Close()
|
||||
hookPrintResults(results)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func hookPrintResults(results []private.HookPostReceiveBranchResult) {
|
||||
for _, res := range results {
|
||||
if !res.Message {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
if res.Create {
|
||||
fmt.Fprintf(os.Stderr, "Create a new pull request for '%s':\n", res.Branch)
|
||||
fmt.Fprintf(os.Stderr, " %s\n", res.URL)
|
||||
} else {
|
||||
fmt.Fprint(os.Stderr, "Visit the existing pull request:\n")
|
||||
fmt.Fprintf(os.Stderr, " %s\n", res.URL)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "")
|
||||
os.Stderr.Sync()
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
20
cmd/keys.go
20
cmd/keys.go
@@ -9,7 +9,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
@@ -40,10 +41,19 @@ var CmdKeys = cli.Command{
|
||||
Value: "",
|
||||
Usage: "Base64 encoded content of the SSH key provided to the SSH Server (requires type to be provided too)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "config, c",
|
||||
Value: "custom/conf/app.ini",
|
||||
Usage: "Custom configuration file path",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func runKeys(c *cli.Context) error {
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
|
||||
if !c.IsSet("username") {
|
||||
return errors.New("No username provided")
|
||||
}
|
||||
@@ -62,12 +72,14 @@ func runKeys(c *cli.Context) error {
|
||||
return errors.New("No key type and content provided")
|
||||
}
|
||||
|
||||
setup("keys.log", false)
|
||||
if err := initDBDisableConsole(true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authorizedString, err := private.AuthorizedPublicKeyByContent(content)
|
||||
publicKey, err := models.SearchPublicKeyByContent(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(strings.TrimSpace(authorizedString))
|
||||
fmt.Println(publicKey.AuthorizedString())
|
||||
return nil
|
||||
}
|
||||
|
454
cmd/manager.go
454
cmd/manager.go
@@ -1,454 +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"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
// CmdManager represents the manager command
|
||||
CmdManager = cli.Command{
|
||||
Name: "manager",
|
||||
Usage: "Manage the running gitea process",
|
||||
Description: "This is a command for managing the running gitea process",
|
||||
Subcommands: []cli.Command{
|
||||
subcmdShutdown,
|
||||
subcmdRestart,
|
||||
subcmdFlushQueues,
|
||||
subcmdLogging,
|
||||
},
|
||||
}
|
||||
subcmdShutdown = cli.Command{
|
||||
Name: "shutdown",
|
||||
Usage: "Gracefully shutdown the running process",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
Action: runShutdown,
|
||||
}
|
||||
subcmdRestart = cli.Command{
|
||||
Name: "restart",
|
||||
Usage: "Gracefully restart the running process - (not implemented for windows servers)",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
Action: runRestart,
|
||||
}
|
||||
subcmdFlushQueues = cli.Command{
|
||||
Name: "flush-queues",
|
||||
Usage: "Flush queues in the running process",
|
||||
Action: runFlushQueues,
|
||||
Flags: []cli.Flag{
|
||||
cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Value: 60 * time.Second,
|
||||
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{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
}
|
||||
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()
|
||||
statusCode, msg := private.RemoveLogger(group, name)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
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")
|
||||
}
|
||||
statusCode, msg := private.AddLogger(group, name, mode, vals)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runShutdown(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.Shutdown()
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runRestart(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.Restart()
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runFlushQueues(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.FlushQueues(c.Duration("timeout"), c.Bool("non-blocking"))
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runPauseLogging(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.PauseLogging()
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runResumeLogging(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.ResumeLogging()
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runReleaseReopenLogging(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.ReleaseReopenLogging()
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
@@ -5,8 +5,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/migrations"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
@@ -21,9 +19,20 @@ var CmdMigrate = cli.Command{
|
||||
Usage: "Migrate the database",
|
||||
Description: "This is a command for migrating the database, so that you can run gitea admin create-user before starting the server.",
|
||||
Action: runMigrate,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "config, c",
|
||||
Value: "custom/conf/app.ini",
|
||||
Usage: "Custom configuration file path",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func runMigrate(ctx *cli.Context) error {
|
||||
if ctx.IsSet("config") {
|
||||
setting.CustomConf = ctx.String("config")
|
||||
}
|
||||
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -32,10 +41,10 @@ func runMigrate(ctx *cli.Context) error {
|
||||
log.Trace("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Trace("Custom path: %s", setting.CustomPath)
|
||||
log.Trace("Log path: %s", setting.LogRootPath)
|
||||
setting.InitDBConfig()
|
||||
models.LoadConfigs()
|
||||
|
||||
if err := models.NewEngine(context.Background(), migrations.Migrate); err != nil {
|
||||
log.Fatal("Failed to initialize ORM engine: %v", err)
|
||||
if err := models.NewEngine(migrations.Migrate); err != nil {
|
||||
log.Fatal(4, "Failed to initialize ORM engine: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@@ -1,190 +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/migrations"
|
||||
"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 models.IterateAttachment(func(attach *models.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 models.IterateUser(func(user *models.User) error {
|
||||
_, err := storage.Copy(dstStorage, user.CustomAvatarRelativePath(), storage.Avatars, user.CustomAvatarRelativePath())
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func migrateRepoAvatars(dstStorage storage.ObjectStorage) error {
|
||||
return models.IterateRepository(func(repo *models.Repository) error {
|
||||
_, err := storage.Copy(dstStorage, repo.CustomAvatarRelativePath(), storage.RepoAvatars, repo.CustomAvatarRelativePath())
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func runMigrateStorage(ctx *cli.Context) error {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("AppPath: %s", setting.AppPath)
|
||||
log.Trace("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Trace("Custom path: %s", setting.CustomPath)
|
||||
log.Trace("Log path: %s", setting.LogRootPath)
|
||||
setting.InitDBConfig()
|
||||
|
||||
if err := models.NewEngine(context.Background(), migrations.Migrate); err != nil {
|
||||
log.Fatal("Failed to initialize ORM engine: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
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 orignial placement.")
|
||||
|
||||
return nil
|
||||
}
|
293
cmd/serv.go
293
cmd/serv.go
@@ -8,29 +8,27 @@ package cmd
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/git"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/lfs"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/pprof"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/kballard/go-shellquote"
|
||||
"github.com/unknwon/com"
|
||||
version "github.com/mcuadros/go-version"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
const (
|
||||
accessDenied = "Repository does not exist or you do not have access"
|
||||
lfsAuthenticateVerb = "git-lfs-authenticate"
|
||||
)
|
||||
|
||||
@@ -41,26 +39,49 @@ var CmdServ = cli.Command{
|
||||
Description: `Serv provide access auth for repositories`,
|
||||
Action: runServ,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "enable-pprof",
|
||||
cli.StringFlag{
|
||||
Name: "config, c",
|
||||
Value: "custom/conf/app.ini",
|
||||
Usage: "Custom configuration file path",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
Name: "enable-pprof",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func setup(logPath string, debug bool) {
|
||||
_ = log.DelLogger("console")
|
||||
if debug {
|
||||
_ = log.NewLogger(1000, "console", "console", `{"level":"trace","stacktracelevel":"NONE","stderr":true}`)
|
||||
} else {
|
||||
_ = log.NewLogger(1000, "console", "console", `{"level":"fatal","stacktracelevel":"NONE","stderr":true}`)
|
||||
func checkLFSVersion() {
|
||||
if setting.LFS.StartServer {
|
||||
//Disable LFS client hooks if installed for the current OS user
|
||||
//Needs at least git v2.1.2
|
||||
binVersion, err := git.BinVersion()
|
||||
if err != nil {
|
||||
fail(fmt.Sprintf("Error retrieving git version: %v", err), fmt.Sprintf("Error retrieving git version: %v", err))
|
||||
}
|
||||
|
||||
if !version.Compare(binVersion, "2.1.2", ">=") {
|
||||
setting.LFS.StartServer = false
|
||||
println("LFS server support needs at least Git v2.1.2, disabled")
|
||||
} else {
|
||||
git.GlobalCommandArgs = append(git.GlobalCommandArgs, "-c", "filter.lfs.required=",
|
||||
"-c", "filter.lfs.smudge=", "-c", "filter.lfs.clean=")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setup(logPath string) {
|
||||
log.DelLogger("console")
|
||||
setting.NewContext()
|
||||
if debug {
|
||||
setting.ProdMode = false
|
||||
checkLFSVersion()
|
||||
log.NewGitLogger(filepath.Join(setting.LogRootPath, logPath))
|
||||
}
|
||||
|
||||
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 (
|
||||
@@ -70,7 +91,6 @@ var (
|
||||
"git-receive-pack": models.AccessModeWrite,
|
||||
lfsAuthenticateVerb: models.AccessModeNone,
|
||||
}
|
||||
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||
)
|
||||
|
||||
func fail(userMessage, logMessage string, args ...interface{}) {
|
||||
@@ -80,14 +100,19 @@ func fail(userMessage, logMessage string, args ...interface{}) {
|
||||
if !setting.ProdMode {
|
||||
fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
|
||||
}
|
||||
log.GitLogger.Fatal(3, logMessage, args...)
|
||||
return
|
||||
}
|
||||
|
||||
log.GitLogger.Close()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func runServ(c *cli.Context) error {
|
||||
// FIXME: This needs to internationalised
|
||||
setup("serv.log", c.Bool("debug"))
|
||||
if c.IsSet("config") {
|
||||
setting.CustomConf = c.String("config")
|
||||
}
|
||||
setup("serv.log")
|
||||
|
||||
if setting.SSH.Disabled {
|
||||
println("Gitea: SSH has been disabled")
|
||||
@@ -95,52 +120,18 @@ func runServ(c *cli.Context) error {
|
||||
}
|
||||
|
||||
if len(c.Args()) < 1 {
|
||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
||||
fmt.Printf("error showing subcommand help: %v\n", err)
|
||||
}
|
||||
cli.ShowSubcommandHelp(c)
|
||||
return nil
|
||||
}
|
||||
|
||||
keys := strings.Split(c.Args()[0], "-")
|
||||
if len(keys) != 2 || keys[0] != "key" {
|
||||
fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
|
||||
}
|
||||
keyID := com.StrTo(keys[1]).MustInt64()
|
||||
|
||||
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
|
||||
if len(cmd) == 0 {
|
||||
key, user, err := private.ServNoCommand(keyID)
|
||||
if err != nil {
|
||||
fail("Internal error", "Failed to check provided key: %v", err)
|
||||
}
|
||||
switch key.Type {
|
||||
case models.KeyTypeDeploy:
|
||||
println("Hi there! You've successfully authenticated with the deploy key named " + key.Name + ", but Gitea does not provide shell access.")
|
||||
case models.KeyTypePrincipal:
|
||||
println("Hi there! You've successfully authenticated with the principal " + key.Content + ", but Gitea does not provide shell access.")
|
||||
default:
|
||||
println("Hi there, " + user.Name + "! You've successfully authenticated with the key named " + key.Name + ", but Gitea does not provide shell access.")
|
||||
}
|
||||
println("Hi there, You've successfully authenticated, but Gitea does not provide shell access.")
|
||||
println("If this is unexpected, please log in with password and setup Gitea under another user.")
|
||||
return nil
|
||||
} else if c.Bool("debug") {
|
||||
log.Debug("SSH_ORIGINAL_COMMAND: %s", os.Getenv("SSH_ORIGINAL_COMMAND"))
|
||||
}
|
||||
|
||||
words, err := shellquote.Split(cmd)
|
||||
if err != nil {
|
||||
fail("Error parsing arguments", "Failed to parse arguments: %v", err)
|
||||
}
|
||||
|
||||
if len(words) < 2 {
|
||||
fail("Too few arguments", "Too few arguments in cmd: %s", cmd)
|
||||
}
|
||||
|
||||
verb := words[0]
|
||||
repoPath := words[1]
|
||||
if repoPath[0] == '/' {
|
||||
repoPath = repoPath[1:]
|
||||
}
|
||||
verb, args := parseCmd(cmd)
|
||||
|
||||
var lfsVerb string
|
||||
if verb == lfsAuthenticateVerb {
|
||||
@@ -148,44 +139,62 @@ func runServ(c *cli.Context) error {
|
||||
fail("Unknown git command", "LFS authentication request over SSH denied, LFS support is disabled")
|
||||
}
|
||||
|
||||
if len(words) > 2 {
|
||||
lfsVerb = words[2]
|
||||
argsSplit := strings.Split(args, " ")
|
||||
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.TrimSpace(repoPath))
|
||||
|
||||
repoPath := strings.ToLower(strings.Trim(args, "'"))
|
||||
rr := strings.SplitN(repoPath, "/", 2)
|
||||
if len(rr) != 2 {
|
||||
fail("Invalid repository path", "Invalid repository path: %v", repoPath)
|
||||
fail("Invalid repository path", "Invalid repository path: %v", args)
|
||||
}
|
||||
|
||||
username := strings.ToLower(rr[0])
|
||||
reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
|
||||
|
||||
if alphaDashDotPattern.MatchString(reponame) {
|
||||
fail("Invalid repo name", "Invalid repo name: %s", reponame)
|
||||
}
|
||||
|
||||
if setting.EnablePprof || c.Bool("enable-pprof") {
|
||||
if err := os.MkdirAll(setting.PprofDataPath, os.ModePerm); err != nil {
|
||||
fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err)
|
||||
}
|
||||
|
||||
stopCPUProfiler, err := pprof.DumpCPUProfileForUsername(setting.PprofDataPath, username)
|
||||
if err != nil {
|
||||
fail("Internal Server Error", "Unable to start CPU profile: %v", err)
|
||||
}
|
||||
stopCPUProfiler := pprof.DumpCPUProfileForUsername(setting.PprofDataPath, username)
|
||||
defer func() {
|
||||
stopCPUProfiler()
|
||||
err := pprof.DumpMemProfileForUsername(setting.PprofDataPath, username)
|
||||
if err != nil {
|
||||
fail("Internal Server Error", "Unable to dump Mem Profile: %v", err)
|
||||
}
|
||||
pprof.DumpMemProfileForUsername(setting.PprofDataPath, username)
|
||||
}()
|
||||
}
|
||||
|
||||
var (
|
||||
isWiki bool
|
||||
unitType = models.UnitTypeCode
|
||||
unitName = "code"
|
||||
)
|
||||
if strings.HasSuffix(reponame, ".wiki") {
|
||||
isWiki = true
|
||||
unitType = models.UnitTypeWiki
|
||||
unitName = "wiki"
|
||||
reponame = reponame[:len(reponame)-5]
|
||||
}
|
||||
|
||||
os.Setenv(models.EnvRepoUsername, username)
|
||||
if isWiki {
|
||||
os.Setenv(models.EnvRepoIsWiki, "true")
|
||||
} else {
|
||||
os.Setenv(models.EnvRepoIsWiki, "false")
|
||||
}
|
||||
os.Setenv(models.EnvRepoName, reponame)
|
||||
|
||||
repo, err := private.GetRepositoryByOwnerAndName(username, reponame)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "Failed to get repository: repository does not exist") {
|
||||
fail(accessDenied, "Repository does not exist: %s/%s", username, reponame)
|
||||
}
|
||||
fail("Internal error", "Failed to get repository: %v", err)
|
||||
}
|
||||
|
||||
requestedMode, has := allowedCommands[verb]
|
||||
if !has {
|
||||
fail("Unknown git command", "Unknown git command %s", verb)
|
||||
@@ -201,43 +210,99 @@ func runServ(c *cli.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
results, err := private.ServCommand(keyID, username, reponame, requestedMode, verb, lfsVerb)
|
||||
if err != nil {
|
||||
if private.IsErrServCommand(err) {
|
||||
errServCommand := err.(private.ErrServCommand)
|
||||
if errServCommand.StatusCode != http.StatusInternalServerError {
|
||||
fail("Unauthorized", "%s", errServCommand.Error())
|
||||
} else {
|
||||
fail("Internal Server Error", "%s", errServCommand.Error())
|
||||
}
|
||||
}
|
||||
fail("Internal Server Error", "%s", err.Error())
|
||||
// Prohibit push to mirror repositories.
|
||||
if requestedMode > models.AccessModeRead && repo.IsMirror {
|
||||
fail("mirror repository is read-only", "")
|
||||
}
|
||||
|
||||
// Allow anonymous clone for public repositories.
|
||||
var (
|
||||
keyID int64
|
||||
user *models.User
|
||||
userID int64
|
||||
)
|
||||
|
||||
keys := strings.Split(c.Args()[0], "-")
|
||||
if len(keys) != 2 {
|
||||
fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
|
||||
}
|
||||
|
||||
key, err := private.GetPublicKeyByID(com.StrTo(keys[1]).MustInt64())
|
||||
if err != nil {
|
||||
fail("Invalid key ID", "Invalid key ID[%s]: %v", c.Args()[0], err)
|
||||
}
|
||||
keyID = key.ID
|
||||
userID = key.OwnerID
|
||||
|
||||
if key.Type == models.KeyTypeDeploy {
|
||||
// Now we have to get the deploy key for this repo
|
||||
deployKey, err := private.GetDeployKey(key.ID, repo.ID)
|
||||
if err != nil {
|
||||
fail("Key access denied", "Failed to access internal api: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
|
||||
}
|
||||
|
||||
if deployKey == nil {
|
||||
fail("Key access denied", "Deploy key access denied: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
|
||||
}
|
||||
|
||||
if deployKey.Mode < requestedMode {
|
||||
fail("Key permission denied", "Cannot push with read-only deployment key: %d to repo_id: %d", key.ID, repo.ID)
|
||||
}
|
||||
|
||||
// Update deploy key activity.
|
||||
if err = private.UpdateDeployKeyUpdated(key.ID, repo.ID); err != nil {
|
||||
fail("Internal error", "UpdateDeployKey: %v", err)
|
||||
}
|
||||
|
||||
// FIXME: Deploy keys aren't really the owner of the repo pushing changes
|
||||
// however we don't have good way of representing deploy keys in hook.go
|
||||
// so for now use the owner
|
||||
os.Setenv(models.EnvPusherName, username)
|
||||
os.Setenv(models.EnvPusherID, fmt.Sprintf("%d", repo.OwnerID))
|
||||
userID = repo.OwnerID
|
||||
} else if requestedMode == models.AccessModeWrite || repo.IsPrivate || setting.Service.RequireSignInView {
|
||||
// Check deploy key or user key.
|
||||
user, err = private.GetUserByKeyID(key.ID)
|
||||
if err != nil {
|
||||
fail("internal error", "Failed to get user by key ID(%d): %v", keyID, err)
|
||||
}
|
||||
|
||||
if !user.IsActive || user.ProhibitLogin {
|
||||
fail("Your account is not active or has been disabled by Administrator",
|
||||
"User %s is disabled and have no access to repository %s",
|
||||
user.Name, repoPath)
|
||||
}
|
||||
|
||||
mode, err := private.CheckUnitUser(user.ID, repo.ID, user.IsAdmin, unitType)
|
||||
if err != nil {
|
||||
fail("Internal error", "Failed to check access: %v", err)
|
||||
} else if *mode < requestedMode {
|
||||
clientMessage := accessDenied
|
||||
if *mode >= models.AccessModeRead {
|
||||
clientMessage = "You do not have sufficient authorization for this action"
|
||||
}
|
||||
fail(clientMessage,
|
||||
"User %s does not have level %v access to repository %s's "+unitName,
|
||||
user.Name, requestedMode, repoPath)
|
||||
}
|
||||
|
||||
os.Setenv(models.EnvPusherName, user.Name)
|
||||
os.Setenv(models.EnvPusherID, fmt.Sprintf("%d", user.ID))
|
||||
}
|
||||
os.Setenv(models.EnvRepoIsWiki, strconv.FormatBool(results.IsWiki))
|
||||
os.Setenv(models.EnvRepoName, results.RepoName)
|
||||
os.Setenv(models.EnvRepoUsername, results.OwnerName)
|
||||
os.Setenv(models.EnvPusherName, results.UserName)
|
||||
os.Setenv(models.EnvPusherEmail, results.UserEmail)
|
||||
os.Setenv(models.EnvPusherID, strconv.FormatInt(results.UserID, 10))
|
||||
os.Setenv(models.EnvRepoID, strconv.FormatInt(results.RepoID, 10))
|
||||
os.Setenv(models.EnvPRID, fmt.Sprintf("%d", 0))
|
||||
os.Setenv(models.EnvIsDeployKey, fmt.Sprintf("%t", results.IsDeployKey))
|
||||
os.Setenv(models.EnvKeyID, fmt.Sprintf("%d", results.KeyID))
|
||||
os.Setenv(models.EnvAppURL, setting.AppURL)
|
||||
|
||||
//LFS token authentication
|
||||
if verb == lfsAuthenticateVerb {
|
||||
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
|
||||
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, username, repo.Name)
|
||||
|
||||
now := time.Now()
|
||||
claims := lfs.Claims{
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
ExpiresAt: now.Add(setting.LFS.HTTPAuthExpiry).Unix(),
|
||||
NotBefore: now.Unix(),
|
||||
},
|
||||
RepoID: results.RepoID,
|
||||
Op: lfsVerb,
|
||||
UserID: results.UserID,
|
||||
claims := jwt.MapClaims{
|
||||
"repo": repo.ID,
|
||||
"op": lfsVerb,
|
||||
"exp": now.Add(setting.LFS.HTTPAuthExpiry).Unix(),
|
||||
"nbf": now.Unix(),
|
||||
}
|
||||
if userID > 0 {
|
||||
claims["user"] = userID
|
||||
}
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
|
||||
@@ -258,6 +323,7 @@ func runServ(c *cli.Context) error {
|
||||
if err != nil {
|
||||
fail("Internal error", "Failed to encode LFS json response: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -273,6 +339,13 @@ func runServ(c *cli.Context) error {
|
||||
} else {
|
||||
gitcmd = exec.Command(verb, repoPath)
|
||||
}
|
||||
if isWiki {
|
||||
if err = private.InitWiki(repo.ID); err != nil {
|
||||
fail("Internal error", "Failed to init wiki repo: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
os.Setenv(models.ProtectedBranchRepoID, fmt.Sprintf("%d", repo.ID))
|
||||
|
||||
gitcmd.Dir = setting.RepoRootPath
|
||||
gitcmd.Stdout = os.Stdout
|
||||
@@ -283,9 +356,9 @@ func runServ(c *cli.Context) error {
|
||||
}
|
||||
|
||||
// Update user key activity.
|
||||
if results.KeyID > 0 {
|
||||
if err = private.UpdatePublicKeyInRepo(results.KeyID, results.RepoID); err != nil {
|
||||
fail("Internal error", "UpdatePublicKeyInRepo: %v", err)
|
||||
if keyID > 0 {
|
||||
if err = private.UpdatePublicKeyUpdated(keyID); err != nil {
|
||||
fail("Internal error", "UpdatePublicKey: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
100
cmd/web.go
100
cmd/web.go
@@ -5,22 +5,22 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/fcgi"
|
||||
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/routers"
|
||||
"code.gitea.io/gitea/routers/routes"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
context2 "github.com/gorilla/context"
|
||||
"github.com/unknwon/com"
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
ini "gopkg.in/ini.v1"
|
||||
@@ -39,9 +39,14 @@ and it takes care of all the other things for you`,
|
||||
Value: "3000",
|
||||
Usage: "Temporary port number to prevent conflict",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "config, c",
|
||||
Value: "custom/conf/app.ini",
|
||||
Usage: "Custom configuration file path",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "pid, P",
|
||||
Value: setting.PIDFile,
|
||||
Value: "/var/run/gitea.pid",
|
||||
Usage: "Custom pid file path",
|
||||
},
|
||||
},
|
||||
@@ -60,10 +65,10 @@ func runHTTPRedirector() {
|
||||
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
||||
})
|
||||
|
||||
var err = runHTTP("tcp", source, context2.ClearHandler(handler))
|
||||
var err = runHTTP(source, context2.ClearHandler(handler))
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("Failed to start port redirection: %v", err)
|
||||
log.Fatal(4, "Failed to start port redirection: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,13 +81,19 @@ func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler)
|
||||
}
|
||||
go func() {
|
||||
log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
|
||||
// all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
|
||||
var err = runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, certManager.HTTPHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)))
|
||||
var err = http.ListenAndServe(setting.HTTPAddr+":"+setting.PortToRedirect, certManager.HTTPHandler(http.HandlerFunc(runLetsEncryptFallbackHandler))) // all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
|
||||
log.Fatal(4, "Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
|
||||
}
|
||||
}()
|
||||
return runHTTPSWithTLSConfig("tcp", listenAddr, certManager.TLSConfig(), context2.ClearHandler(m))
|
||||
server := &http.Server{
|
||||
Addr: listenAddr,
|
||||
Handler: m,
|
||||
TLSConfig: &tls.Config{
|
||||
GetCertificate: certManager.GetCertificate,
|
||||
},
|
||||
}
|
||||
return server.ListenAndServeTLS("", "")
|
||||
}
|
||||
|
||||
func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -93,31 +104,21 @@ func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// 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()
|
||||
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())
|
||||
graceful.InitManager(managerCtx)
|
||||
defer cancel()
|
||||
|
||||
if os.Getppid() > 1 && len(os.Getenv("LISTEN_FDS")) > 0 {
|
||||
log.Info("Restarting Gitea on PID: %d from parent PID: %d", os.Getpid(), os.Getppid())
|
||||
} else {
|
||||
log.Info("Starting Gitea on PID: %d", os.Getpid())
|
||||
if ctx.IsSet("config") {
|
||||
setting.CustomConf = ctx.String("config")
|
||||
}
|
||||
|
||||
// Set pid file setting
|
||||
if ctx.IsSet("pid") {
|
||||
setting.PIDFile = ctx.String("pid")
|
||||
setting.WritePIDFile = true
|
||||
setting.CustomPID = ctx.String("pid")
|
||||
}
|
||||
|
||||
// Perform global initialization
|
||||
routers.GlobalInit(graceful.GetManager().HammerContext())
|
||||
routers.GlobalInit()
|
||||
|
||||
// Set up Macaron
|
||||
m := routes.NewMacaron()
|
||||
routes.RegisterRoutes(m)
|
||||
|
||||
@@ -129,7 +130,6 @@ func runWeb(ctx *cli.Context) error {
|
||||
switch setting.Protocol {
|
||||
case setting.UnixSocket:
|
||||
case setting.FCGI:
|
||||
case setting.FCGIUnix:
|
||||
default:
|
||||
// Save LOCAL_ROOT_URL if port changed
|
||||
cfg := ini.Empty()
|
||||
@@ -157,8 +157,8 @@ func runWeb(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
listenAddr := setting.HTTPAddr
|
||||
if setting.Protocol != setting.UnixSocket && setting.Protocol != setting.FCGIUnix {
|
||||
listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort)
|
||||
if setting.Protocol != setting.UnixSocket {
|
||||
listenAddr += ":" + setting.HTTPPort
|
||||
}
|
||||
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
|
||||
|
||||
@@ -176,8 +176,7 @@ func runWeb(ctx *cli.Context) error {
|
||||
var err error
|
||||
switch setting.Protocol {
|
||||
case setting.HTTP:
|
||||
NoHTTPRedirector()
|
||||
err = runHTTP("tcp", listenAddr, context2.ClearHandler(m))
|
||||
err = runHTTP(listenAddr, context2.ClearHandler(m))
|
||||
case setting.HTTPS:
|
||||
if setting.EnableLetsEncrypt {
|
||||
err = runLetsEncrypt(listenAddr, setting.Domain, setting.LetsEncryptDirectory, setting.LetsEncryptEmail, context2.ClearHandler(m))
|
||||
@@ -185,29 +184,38 @@ func runWeb(ctx *cli.Context) error {
|
||||
}
|
||||
if setting.RedirectOtherPort {
|
||||
go runHTTPRedirector()
|
||||
} else {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runHTTPS("tcp", listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
|
||||
err = runHTTPS(listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
|
||||
case setting.FCGI:
|
||||
NoHTTPRedirector()
|
||||
err = runFCGI("tcp", listenAddr, context2.ClearHandler(m))
|
||||
listener, err := net.Listen("tcp", listenAddr)
|
||||
if err != nil {
|
||||
log.Fatal(4, "Failed to bind %s", listenAddr, err)
|
||||
}
|
||||
defer listener.Close()
|
||||
err = fcgi.Serve(listener, context2.ClearHandler(m))
|
||||
case setting.UnixSocket:
|
||||
NoHTTPRedirector()
|
||||
err = runHTTP("unix", listenAddr, context2.ClearHandler(m))
|
||||
case setting.FCGIUnix:
|
||||
NoHTTPRedirector()
|
||||
err = runFCGI("unix", listenAddr, context2.ClearHandler(m))
|
||||
if err := os.Remove(listenAddr); err != nil && !os.IsNotExist(err) {
|
||||
log.Fatal(4, "Failed to remove unix socket directory %s: %v", listenAddr, err)
|
||||
}
|
||||
var listener *net.UnixListener
|
||||
listener, err = net.ListenUnix("unix", &net.UnixAddr{Name: listenAddr, Net: "unix"})
|
||||
if err != nil {
|
||||
break // Handle error after switch
|
||||
}
|
||||
|
||||
// FIXME: add proper implementation of signal capture on all protocols
|
||||
// execute this on SIGTERM or SIGINT: listener.Close()
|
||||
if err = os.Chmod(listenAddr, os.FileMode(setting.UnixSocketPermission)); err != nil {
|
||||
log.Fatal(4, "Failed to set permission of unix socket: %v", err)
|
||||
}
|
||||
err = http.Serve(listener, context2.ClearHandler(m))
|
||||
default:
|
||||
log.Fatal("Invalid protocol: %s", setting.Protocol)
|
||||
log.Fatal(4, "Invalid protocol: %s", setting.Protocol)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Critical("Failed to start server: %v", err)
|
||||
log.Fatal(4, "Failed to start server: %v", err)
|
||||
}
|
||||
log.Info("HTTP Listener: %s Closed", listenAddr)
|
||||
<-graceful.GetManager().Done()
|
||||
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
||||
log.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
// +build !windows
|
||||
|
||||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
@@ -6,47 +8,38 @@ package cmd
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/fcgi"
|
||||
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"github.com/facebookgo/grace/gracehttp"
|
||||
)
|
||||
|
||||
func runHTTP(network, listenAddr string, m http.Handler) error {
|
||||
return graceful.HTTPListenAndServe(network, listenAddr, m)
|
||||
}
|
||||
|
||||
func runHTTPS(network, listenAddr, certFile, keyFile string, m http.Handler) error {
|
||||
return graceful.HTTPListenAndServeTLS(network, listenAddr, certFile, keyFile, m)
|
||||
}
|
||||
|
||||
func runHTTPSWithTLSConfig(network, listenAddr string, tlsConfig *tls.Config, m http.Handler) error {
|
||||
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, tlsConfig, m)
|
||||
}
|
||||
|
||||
// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
|
||||
func NoHTTPRedirector() {
|
||||
graceful.GetManager().InformCleanup()
|
||||
}
|
||||
|
||||
// NoMainListener tells our cleanup routine that we will not be using a possibly provided listener
|
||||
// for our main HTTP/HTTPS service
|
||||
func NoMainListener() {
|
||||
graceful.GetManager().InformCleanup()
|
||||
}
|
||||
|
||||
func runFCGI(network, listenAddr string, m http.Handler) error {
|
||||
// This needs to handle stdin as fcgi point
|
||||
fcgiServer := graceful.NewServer(network, listenAddr)
|
||||
|
||||
err := fcgiServer.ListenAndServe(func(listener net.Listener) error {
|
||||
return fcgi.Serve(listener, m)
|
||||
func runHTTP(listenAddr string, m http.Handler) error {
|
||||
return gracehttp.Serve(&http.Server{
|
||||
Addr: listenAddr,
|
||||
Handler: m,
|
||||
})
|
||||
}
|
||||
|
||||
func runHTTPS(listenAddr, certFile, keyFile string, m http.Handler) error {
|
||||
config := &tls.Config{
|
||||
MinVersion: tls.VersionTLS10,
|
||||
}
|
||||
if config.NextProtos == nil {
|
||||
config.NextProtos = []string{"http/1.1"}
|
||||
}
|
||||
|
||||
config.Certificates = make([]tls.Certificate, 1)
|
||||
var err error
|
||||
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
log.Fatal(4, "Failed to load https cert file %s: %v", listenAddr, err)
|
||||
}
|
||||
|
||||
return gracehttp.Serve(&http.Server{
|
||||
Addr: listenAddr,
|
||||
Handler: m,
|
||||
TLSConfig: config,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal("Failed to start FCGI main server: %v", err)
|
||||
}
|
||||
log.Info("FCGI Listener: %s Closed", listenAddr)
|
||||
return err
|
||||
}
|
||||
|
19
cmd/web_windows.go
Normal file
19
cmd/web_windows.go
Normal file
@@ -0,0 +1,19 @@
|
||||
// +build windows
|
||||
|
||||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func runHTTP(listenAddr string, m http.Handler) error {
|
||||
return http.ListenAndServe(listenAddr, m)
|
||||
}
|
||||
|
||||
func runHTTPS(listenAddr, certFile, keyFile string, m http.Handler) error {
|
||||
return http.ListenAndServeTLS(listenAddr, certFile, keyFile, m)
|
||||
}
|
@@ -1,66 +0,0 @@
|
||||
Environment To Ini
|
||||
==================
|
||||
|
||||
Multiple docker users have requested that the Gitea docker is changed
|
||||
to permit arbitrary configuration via environment variables.
|
||||
|
||||
Gitea needs to use an ini file for configuration because the running
|
||||
environment that starts the docker may not be the same as that used
|
||||
by the hooks. An ini file also gives a good default and means that
|
||||
users do not have to completely provide a full environment.
|
||||
|
||||
With those caveats above, this command provides a generic way of
|
||||
converting suitably structured environment variables into any ini
|
||||
value.
|
||||
|
||||
To use the command is very simple just run it and the default gitea
|
||||
app.ini will be rewritten to take account of the variables provided,
|
||||
however there are various options to give slightly different
|
||||
behavior and these can be interrogated with the `-h` option.
|
||||
|
||||
The environment variables should be of the form:
|
||||
|
||||
GITEA__SECTION_NAME__KEY_NAME
|
||||
|
||||
Environment variables are usually restricted to a reduced character
|
||||
set "0-9A-Z_" - in order to allow the setting of sections with
|
||||
characters outside of that set, they should be escaped as following:
|
||||
"_0X2E_" for ".". The entire section and key names can be escaped as
|
||||
a UTF8 byte string if necessary. E.g. to configure:
|
||||
|
||||
"""
|
||||
...
|
||||
[log.console]
|
||||
COLORIZE=false
|
||||
STDERR=true
|
||||
...
|
||||
"""
|
||||
|
||||
You would set the environment variables: "GITEA__LOG_0x2E_CONSOLE__COLORIZE=false"
|
||||
and "GITEA__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found
|
||||
on the configuration cheat sheet.
|
||||
|
||||
To plug this command in to the docker, you simply compile the provided go file using:
|
||||
|
||||
go build environment-to-ini.go
|
||||
|
||||
And copy the resulting `environment-to-ini` command to /app/gitea in the docker.
|
||||
|
||||
Apply the below patch to /etc/s6/gitea.setup to wire this in.
|
||||
|
||||
If you find this useful please comment on #7287
|
||||
|
||||
|
||||
diff --git a/docker/root/etc/s6/gitea/setup b/docker/root/etc/s6/gitea/setup
|
||||
index f87ce9115..565bfcba9 100755
|
||||
--- a/docker/root/etc/s6/gitea/setup
|
||||
+++ b/docker/root/etc/s6/gitea/setup
|
||||
@@ -44,6 +44,8 @@ if [ ! -f ${GITEA_CUSTOM}/conf/app.ini ]; then
|
||||
SECRET_KEY=${SECRET_KEY:-""} \
|
||||
envsubst < /etc/templates/app.ini > ${GITEA_CUSTOM}/conf/app.ini
|
||||
|
||||
+ /app/gitea/environment-to-ini -c ${GITEA_CUSTOM}/conf/app.ini
|
||||
+
|
||||
chown ${USER}:git ${GITEA_CUSTOM}/conf/app.ini
|
||||
fi
|
||||
|
@@ -1,224 +0,0 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/unknwon/com"
|
||||
"github.com/urfave/cli"
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
// EnvironmentPrefix environment variables prefixed with this represent ini values to write
|
||||
const EnvironmentPrefix = "GITEA"
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "environment-to-ini"
|
||||
app.Usage = "Use provided environment to update configuration ini"
|
||||
app.Description = `As a helper to allow docker users to update the gitea configuration
|
||||
through the environment, this command allows environment variables to
|
||||
be mapped to values in the ini.
|
||||
|
||||
Environment variables of the form "GITEA__SECTION_NAME__KEY_NAME"
|
||||
will be mapped to the ini section "[section_name]" and the key
|
||||
"KEY_NAME" with the value as provided.
|
||||
|
||||
Environment variables are usually restricted to a reduced character
|
||||
set "0-9A-Z_" - in order to allow the setting of sections with
|
||||
characters outside of that set, they should be escaped as following:
|
||||
"_0X2E_" for ".". The entire section and key names can be escaped as
|
||||
a UTF8 byte string if necessary. E.g. to configure:
|
||||
|
||||
"""
|
||||
...
|
||||
[log.console]
|
||||
COLORIZE=false
|
||||
STDERR=true
|
||||
...
|
||||
"""
|
||||
|
||||
You would set the environment variables: "GITEA__LOG_0x2E_CONSOLE__COLORIZE=false"
|
||||
and "GITEA__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found
|
||||
on the configuration cheat sheet.`
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "custom-path, C",
|
||||
Value: setting.CustomPath,
|
||||
Usage: "Custom path file path",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "config, c",
|
||||
Value: setting.CustomConf,
|
||||
Usage: "Custom configuration file path",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "work-path, w",
|
||||
Value: setting.AppWorkPath,
|
||||
Usage: "Set the gitea working path",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "out, o",
|
||||
Value: "",
|
||||
Usage: "Destination file to write to",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "clear",
|
||||
Usage: "Clears the matched variables from the environment",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "prefix, p",
|
||||
Value: EnvironmentPrefix,
|
||||
Usage: "Environment prefix to look for - will be suffixed by __ (2 underscores)",
|
||||
},
|
||||
}
|
||||
app.Action = runEnvironmentToIni
|
||||
setting.SetCustomPathAndConf("", "", "")
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to run app with %s: %v", os.Args, err)
|
||||
}
|
||||
}
|
||||
|
||||
func runEnvironmentToIni(c *cli.Context) error {
|
||||
providedCustom := c.String("custom-path")
|
||||
providedConf := c.String("config")
|
||||
providedWorkPath := c.String("work-path")
|
||||
setting.SetCustomPathAndConf(providedCustom, providedConf, providedWorkPath)
|
||||
|
||||
cfg := ini.Empty()
|
||||
if com.IsFile(setting.CustomConf) {
|
||||
if err := cfg.Append(setting.CustomConf); err != nil {
|
||||
log.Fatal("Failed to load custom conf '%s': %v", setting.CustomConf, err)
|
||||
}
|
||||
} else {
|
||||
log.Warn("Custom config '%s' not found, ignore this if you're running first time", setting.CustomConf)
|
||||
}
|
||||
cfg.NameMapper = ini.SnackCase
|
||||
|
||||
prefix := c.String("prefix") + "__"
|
||||
|
||||
for _, kv := range os.Environ() {
|
||||
idx := strings.IndexByte(kv, '=')
|
||||
if idx < 0 {
|
||||
continue
|
||||
}
|
||||
eKey := kv[:idx]
|
||||
value := kv[idx+1:]
|
||||
if !strings.HasPrefix(eKey, prefix) {
|
||||
continue
|
||||
}
|
||||
eKey = eKey[len(prefix):]
|
||||
sectionName, keyName := DecodeSectionKey(eKey)
|
||||
if len(keyName) == 0 {
|
||||
continue
|
||||
}
|
||||
section, err := cfg.GetSection(sectionName)
|
||||
if err != nil {
|
||||
section, err = cfg.NewSection(sectionName)
|
||||
if err != nil {
|
||||
log.Error("Error creating section: %s : %v", sectionName, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
key := section.Key(keyName)
|
||||
if key == nil {
|
||||
key, err = section.NewKey(keyName, value)
|
||||
if err != nil {
|
||||
log.Error("Error creating key: %s in section: %s with value: %s : %v", keyName, sectionName, value, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
key.SetValue(value)
|
||||
}
|
||||
destination := c.String("out")
|
||||
if len(destination) == 0 {
|
||||
destination = setting.CustomConf
|
||||
}
|
||||
err := cfg.SaveTo(destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Bool("clear") {
|
||||
for _, kv := range os.Environ() {
|
||||
idx := strings.IndexByte(kv, '=')
|
||||
if idx < 0 {
|
||||
continue
|
||||
}
|
||||
eKey := kv[:idx]
|
||||
if strings.HasPrefix(eKey, prefix) {
|
||||
_ = os.Unsetenv(eKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const escapeRegexpString = "_0[xX](([0-9a-fA-F][0-9a-fA-F])+)_"
|
||||
|
||||
var escapeRegex = regexp.MustCompile(escapeRegexpString)
|
||||
|
||||
// DecodeSectionKey will decode a portable string encoded Section__Key pair
|
||||
// Portable strings are considered to be of the form [A-Z0-9_]*
|
||||
// We will encode a disallowed value as the UTF8 byte string preceded by _0X and
|
||||
// followed by _. E.g. _0X2C_ for a '-' and _0X2E_ for '.'
|
||||
// Section and Key are separated by a plain '__'.
|
||||
// The entire section can be encoded as a UTF8 byte string
|
||||
func DecodeSectionKey(encoded string) (string, string) {
|
||||
section := ""
|
||||
key := ""
|
||||
|
||||
inKey := false
|
||||
last := 0
|
||||
escapeStringIndices := escapeRegex.FindAllStringIndex(encoded, -1)
|
||||
for _, unescapeIdx := range escapeStringIndices {
|
||||
preceding := encoded[last:unescapeIdx[0]]
|
||||
if !inKey {
|
||||
if splitter := strings.Index(preceding, "__"); splitter > -1 {
|
||||
section += preceding[:splitter]
|
||||
inKey = true
|
||||
key += preceding[splitter+2:]
|
||||
} else {
|
||||
section += preceding
|
||||
}
|
||||
} else {
|
||||
key += preceding
|
||||
}
|
||||
toDecode := encoded[unescapeIdx[0]+3 : unescapeIdx[1]-1]
|
||||
decodedBytes := make([]byte, len(toDecode)/2)
|
||||
for i := 0; i < len(toDecode)/2; i++ {
|
||||
// Can ignore error here as we know these should be hexadecimal from the regexp
|
||||
byteInt, _ := strconv.ParseInt(toDecode[2*i:2*i+2], 16, 0)
|
||||
decodedBytes[i] = byte(byteInt)
|
||||
}
|
||||
if inKey {
|
||||
key += string(decodedBytes)
|
||||
} else {
|
||||
section += string(decodedBytes)
|
||||
}
|
||||
last = unescapeIdx[1]
|
||||
}
|
||||
remaining := encoded[last:]
|
||||
if !inKey {
|
||||
if splitter := strings.Index(remaining, "__"); splitter > -1 {
|
||||
section += remaining[:splitter]
|
||||
inKey = true
|
||||
key += remaining[splitter+2:]
|
||||
} else {
|
||||
section += remaining
|
||||
}
|
||||
} else {
|
||||
key += remaining
|
||||
}
|
||||
return section, key
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
########################################################################
|
||||
# This script some defaults for gitea to run in a FHS compliant manner #
|
||||
########################################################################
|
||||
|
||||
# It assumes that you place this script as gitea in /usr/bin
|
||||
#
|
||||
# And place the original in /usr/lib/gitea with working files in /var/lib/gitea
|
||||
# and main configuration in /etc/gitea/app.ini
|
||||
GITEA="/usr/lib/gitea/gitea"
|
||||
WORK_DIR="/var/lib/gitea"
|
||||
APP_INI="/etc/gitea/app.ini"
|
||||
|
||||
APP_INI_SET=""
|
||||
for i in "$@"; do
|
||||
case "$i" in
|
||||
"-c")
|
||||
APP_INI_SET=1
|
||||
;;
|
||||
"-c="*)
|
||||
APP_INI_SET=1
|
||||
;;
|
||||
"--config")
|
||||
APP_INI_SET=1
|
||||
;;
|
||||
"--config="*)
|
||||
APP_INI_SET=1
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$APP_INI_SET" ]; then
|
||||
CONF_ARG="-c \"$APP_INI\""
|
||||
fi
|
||||
|
||||
# Provide FHS compliant defaults to
|
||||
GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" "$GITEA" $CONF_ARG "$@"
|
||||
|
||||
|
@@ -1,76 +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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
)
|
||||
|
||||
// To generate derivative fixtures, execute the following from Gitea's repository base dir:
|
||||
// go run -tags 'sqlite sqlite_unlock_notify' contrib/fixtures/fixture_generation.go [fixture...]
|
||||
|
||||
var (
|
||||
generators = []struct {
|
||||
gen func() (string, error)
|
||||
name string
|
||||
}{
|
||||
{
|
||||
models.GetYamlFixturesAccess, "access",
|
||||
},
|
||||
}
|
||||
fixturesDir string
|
||||
)
|
||||
|
||||
func main() {
|
||||
pathToGiteaRoot := "."
|
||||
fixturesDir = filepath.Join(pathToGiteaRoot, "models", "fixtures")
|
||||
if err := models.CreateTestEngine(fixturesDir); err != nil {
|
||||
fmt.Printf("CreateTestEngine: %+v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := models.PrepareTestDatabase(); err != nil {
|
||||
fmt.Printf("PrepareTestDatabase: %+v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(os.Args) == 0 {
|
||||
for _, r := range os.Args {
|
||||
if err := generate(r); err != nil {
|
||||
fmt.Printf("generate '%s': %+v\n", r, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, g := range generators {
|
||||
if err := generate(g.name); err != nil {
|
||||
fmt.Printf("generate '%s': %+v\n", g.name, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generate(name string) error {
|
||||
for _, g := range generators {
|
||||
if g.name == name {
|
||||
data, err := g.gen()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := filepath.Join(fixturesDir, name+".yml")
|
||||
if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil {
|
||||
return fmt.Errorf("%s: %+v", path, err)
|
||||
}
|
||||
fmt.Printf("%s created.\n", path)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("generator not found")
|
||||
}
|
@@ -2,7 +2,7 @@
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build",
|
||||
"taskName": "Build",
|
||||
"type": "shell",
|
||||
"command": "go",
|
||||
"group": "build",
|
||||
@@ -12,19 +12,20 @@
|
||||
"focus": false,
|
||||
"panel": "shared"
|
||||
},
|
||||
"args": ["build"],
|
||||
"linux": {
|
||||
"args": ["build", "-o", "gitea", "${workspaceRoot}/main.go" ]
|
||||
"args": [ "-o", "gitea", "${workspaceRoot}/main.go" ]
|
||||
},
|
||||
"osx": {
|
||||
"args": ["build", "-o", "gitea", "${workspaceRoot}/main.go" ]
|
||||
"args": [ "-o", "gitea", "${workspaceRoot}/main.go" ]
|
||||
},
|
||||
"windows": {
|
||||
"args": ["build", "-o", "gitea.exe", "\"${workspaceRoot}\\main.go\""]
|
||||
"args": [ "-o", "gitea.exe", "\"${workspaceRoot}\\main.go\""]
|
||||
},
|
||||
"problemMatcher": ["$go"]
|
||||
},
|
||||
{
|
||||
"label": "Build (with SQLite3)",
|
||||
"taskName": "Build (with SQLite3)",
|
||||
"type": "shell",
|
||||
"command": "go",
|
||||
"group": "build",
|
||||
@@ -34,14 +35,15 @@
|
||||
"focus": false,
|
||||
"panel": "shared"
|
||||
},
|
||||
"args": ["build", "-tags=\"sqlite sqlite_unlock_notify\""],
|
||||
"linux": {
|
||||
"args": ["build", "-tags=\"sqlite sqlite_unlock_notify\"", "-o", "gitea", "${workspaceRoot}/main.go"]
|
||||
"args": ["-o", "gitea", "${workspaceRoot}/main.go"]
|
||||
},
|
||||
"osx": {
|
||||
"args": ["build", "-tags=\"sqlite sqlite_unlock_notify\"", "-o", "gitea", "${workspaceRoot}/main.go"]
|
||||
"args": ["-o", "gitea", "${workspaceRoot}/main.go"]
|
||||
},
|
||||
"windows": {
|
||||
"args": ["build", "-tags=\"sqlite sqlite_unlock_notify\"", "-o", "gitea.exe", "\"${workspaceRoot}\\main.go\""]
|
||||
"args": ["-o", "gitea.exe", "\"${workspaceRoot}\\main.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"
|
||||
NAME=gitea
|
||||
SERVICEVERBOSE=yes
|
||||
PIDFILE=/run/$NAME.pid
|
||||
PIDFILE=/var/run/$NAME.pid
|
||||
SCRIPTNAME=/etc/init.d/$NAME
|
||||
WORKINGDIR=/var/lib/$NAME
|
||||
DAEMON=/usr/local/bin/$NAME
|
||||
|
@@ -7,7 +7,7 @@ start_stop_daemon_args="--user ${USER} --chdir ${DIR}"
|
||||
command="/usr/local/bin/gitea"
|
||||
command_args="web -c /etc/gitea/app.ini"
|
||||
command_background=yes
|
||||
pidfile=/run/gitea.pid
|
||||
pidfile=/var/run/gitea.pid
|
||||
|
||||
depend()
|
||||
{
|
||||
|
@@ -93,7 +93,7 @@ case "$1" in
|
||||
|
||||
# Return value is slightly different for the status command:
|
||||
# 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
|
||||
# 3 - service not running (unused)
|
||||
# 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
|
@@ -1,196 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Privacy Policy</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Privacy Policy</h1>
|
||||
|
||||
<h4>Last updated: January 29, 2020</h4>
|
||||
|
||||
<h2>Who We Are?</h2>
|
||||
|
||||
<p>Your Gitea Instance</p>
|
||||
|
||||
<h2>What Personal Data We Collect?</h2>
|
||||
|
||||
<p>We collect following personal data (collectively called User Personal Information):</p>
|
||||
|
||||
<ol>
|
||||
<li>Registration information (username, email, password, etc.)</li>
|
||||
<li>Profile information for your Account (such as your full name, biography, website, gpg key, and location.)</li>
|
||||
<li>Usage information (pages you view, your IP address, referring site, session information, and request date and time.)</li>
|
||||
<li>Device information (its IP address, client application information, language preference, operating system and application version, device type, ID, model and manufacturer.)</li>
|
||||
<li>Git data that you upload to a repository</li>
|
||||
<li>Cookies and Similar Technologies</li>
|
||||
</ol>
|
||||
|
||||
<p>We may also collect User Personal Information from third-parties (vendors, partners, or affiliates). We don't purchase them from third-party data brokers, though.</p>
|
||||
|
||||
<p>However, we don't intentionally collect sensitive information (such as racial or ethnic origin, political affiliations, religious/philosophical beliefs, biometric data, etc.)</p>
|
||||
<!--If you choose to store any of such data on our servers, you are responsible for complying with any regulations regarding them.-->
|
||||
|
||||
<h2>How We Share Information We Collect?</h2>
|
||||
|
||||
<p>We may share your User Personal Information with third-parties under following circumstances:</p>
|
||||
|
||||
<h3>With your Consent</h3>
|
||||
|
||||
<p>We share your User Personal Information, if you consent, after letting you know what information will be shared, with whom, and why. For example, if you allow third party applications to access your Account using <a href="https://docs.gitea.io/en-us/oauth2-provider/">OAuth2 providers</a>, we share all information associated with your Account, including private repos and organizations. You may also direct us through your action on Your Gitea Instance to share your User Personal Information, such as when joining an Organization.</p>
|
||||
|
||||
<h3>With Service Providers</h3>
|
||||
|
||||
<p>We share User Personal Information with a limited number of service providers who process it on our behalf to provide or improve our Service, and who have agreed to privacy restrictions similar to the ones in our Privacy Statement by signing data protection agreements or making similar commitments. Our service providers perform payment processing, customer support ticketing, network data transmission, security, and other similar services. While Your Gitea Instance processes all User Personal Information in the (country/state where Gitea is deployed), our service providers may process data outside of (country/state where Gitea is deployed), the United States or the European Union.</p>
|
||||
|
||||
<h3>For Security Purposes</h3>
|
||||
|
||||
<p>If you are a member of an Organization, Your Gitea Instance may share your username, Usage Information, and Device Information associated with that Organization with an owner and/or administrator of the Organization who has agreed to the Corporate Terms of Service or applicable customer agreements, to the extent that such information is provided only to investigate or respond to a security incident that affects or compromises the security of that particular Organization.</p>
|
||||
|
||||
<h3>For Legal Disclosure</h3>
|
||||
|
||||
<p>Your Gitea Instance strives for transparency in complying with legal process and legal obligations. Unless prevented from doing so by law or court order, or in rare, exigent circumstances, we make a reasonable effort to notify users of any legally compelled or required disclosure of their information. Your Gitea Instance may disclose User Personal Information or other information we collect about you to law enforcement if required in response to a valid subpoena, court order, search warrant, a similar government order, or when we believe in good faith that disclosure is necessary to comply with our legal obligations, to protect our property or rights, or those of third parties or the public at large.</p>
|
||||
|
||||
<h3>Change in Control or Sale</h3>
|
||||
|
||||
<p>We may share User Personal Information if we are involved in a merger, sale, or acquisition of corporate entities or business units. If any such change of ownership happens, we will ensure that it is under terms that preserve the confidentiality of User Personal Information, and we will notify you on our Website or by email before any transfer of your User Personal Information. The organization receiving any User Personal Information will have to honor any promises we made in our Privacy Statement or Terms of Service.</p>
|
||||
|
||||
<h3>Aggregate, Non-Personally Identifying Information</h3>
|
||||
|
||||
<p>We share certain aggregated, non-personally identifying information with others about how our users, collectively, use Your Gitea Instance, or how our users respond to our other offerings, such as our conferences or events. For example, we may compile statistics on the open source activity across Your Gitea Instance.</p>
|
||||
|
||||
<p>We <b>don't</b> sell your User Personal Information for monetary or other consideration. </p>
|
||||
|
||||
<h2>How We Use Your Information?</h2>
|
||||
|
||||
<p>We may use your information for following purposes:</p>
|
||||
|
||||
<ol>
|
||||
<li>We use your Registration Information to create your account, and to provide you the Service.</li>
|
||||
<li>We use your User Personal Information, specifically your username, to identify you on Your Gitea Instance.</li>
|
||||
<li>We use your Profile Information to fill out your Account profile and to share that profile with other users if you ask us to.</li>
|
||||
<li>We use your email address to communicate with you, if you've said that's okay, and only for the reasons you’ve said that’s okay.</li>
|
||||
<li>We use User Personal Information and other data to make recommendations for you, such as to suggest projects you may want to follow or contribute to. We learn from your public behavior on Your Gitea Instance—such as the projects you star—to determine your coding interests, and we recommend similar projects. These recommendations are automated decisions, but they have no legal impact on your rights.</li>
|
||||
<li>We use Usage Information and Device Information to better understand how our Users use Your Gitea Instance and to improve our Website and Service.</li>
|
||||
<li>We may use your User Personal Information if it is necessary for security purposes or to investigate possible fraud or attempts to harm Your Gitea Instance or our Users.</li>
|
||||
<li>We may use your User Personal Information to comply with our legal obligations, protect our intellectual property, and enforce our Terms of Service.</li>
|
||||
|
||||
<li>We limit our use of your User Personal Information to the purposes listed in this Privacy Statement. If we need to use your User Personal Information for other purposes, we will ask your permission first. You can always see what information we have, how we're using it, and what permissions you have given us in your user profile.</li>
|
||||
</ol>
|
||||
|
||||
<h2>How Your Gitea Instance Secures Your Information?</h2>
|
||||
|
||||
<p>Your Gitea Instance takes all measures reasonably necessary to protect User Personal Information from unauthorized access, alteration, or destruction; maintain data accuracy; and help ensure the appropriate use of User Personal Information.</p>
|
||||
|
||||
<p>To the extent above, we enforce a written security information program, which:</p>
|
||||
|
||||
<ul>
|
||||
<li>aligns with industry recognized frameworks;</li>
|
||||
<li>includes security safeguards reasonably designed to protect the confidentiality, integrity, availability, and resilience of our Users' data;</li>
|
||||
<li>is appropriate to the nature, size, and complexity of Your Gitea Instance’s business operations;</li>
|
||||
<li>includes incident response and data breach notification processes; and</li>
|
||||
<li>complies with applicable information security-related laws and regulations in the geographic regions where Your Gitea Instance does business.</li>
|
||||
</ul>
|
||||
|
||||
<p>In the event of a data breach that affects your User Personal Information, we will act promptly to mitigate the impact of a breach and notify any affected Users without undue delay.</p>
|
||||
|
||||
<p>Transmission of data on Your Gitea Instance is encrypted using SSH, HTTPS (TLS), and git repository content is encrypted at rest. We host Your Gitea Instance at our hosting partner, which they provide data centers with high level of physical and network security.</p>
|
||||
|
||||
<p><b>Disclaimer:</b> No method of transmission, or method of electronic storage, is 100% secure, therefore, we cannot guarantee absolute security.</p>
|
||||
|
||||
<h2>Cookies and Tracking Usage</h2>
|
||||
|
||||
<h3>Cookies</h3>
|
||||
|
||||
<p>We uses cookies to make interactions with our service easy and meaningful. Cookies are small text files that websites often store on computer hard drives or mobile devices of visitors. We use cookies (and similar technologies, like HTML5 localStorage) to keep you logged in, remember your preferences, and provide information for future development of Your Gitea Instance. For security purposes, we use cookies to identify a device. By using our Website, you agree that we can place these types of cookies on your computer or device. If you disable your browser or device’s ability to accept these cookies, you will not be able to log in or use our services.</p>
|
||||
|
||||
<h3>Tracking and Analytics</h3>
|
||||
|
||||
<p>Out of the box, Gitea doesn't use third-party analytics. In case when we opt in to their usage, we do that to help us evaluate our Users' use of Your Gitea Instance, compile statistical reports on activity, and improve our content and Website performance. We only use these third-party analytics providers on certain areas of our Website, and all of them have signed data protection agreements with us that limit the type of User Personal Information they can collect and the purpose for which they can process the information. In addition, we may also deploy internal analytics software to provide similar functionality.</p>
|
||||
|
||||
<p>Some browsers have incorporated "Do Not Track" (DNT) features that can send a signal to the websites you visit indicating you do not wish to be tracked. Your Gitea Instance responds to browser DNT signals and follows the <a href="https://www.w3.org/TR/tracking-dnt/">W3C standard for responding to DNT signals</a>. If you have not enabled DNT on a browser that supports it, cookies on some parts of our Website will track your online browsing activity on other online services over time, though we do not permit third parties other than our analytics and service providers to track Your Gitea Instance Users' activity over time on Your Gitea Instance.</p>
|
||||
|
||||
<h2>Repository Contents</h2>
|
||||
|
||||
<p>Our employees do not access private repositories unless required to for security purposes, for support, to maintain integrity of the Service, or to comply with our legal obligations. While we don't generally search for content in your repositories, we may scan our servers and your content to detect tokens or security signatures, known malwares, or child exploitation imagery.</p>
|
||||
|
||||
<p>If your repository is public, anyone may view its contents. If you include private, confidential or Sensitive Personal Information, such as email addresses or passwords, in your public repository, that information may be indexed by search engines or used by third parties.</p>
|
||||
|
||||
<h2>Public Information</h2>
|
||||
|
||||
<p>Many of our services and feature are public-facing. If your content is public-facing, third parties may access and use it in compliance with our Terms of Service, such as by viewing your profile or repositories or pulling data via our API. We do not sell that content; it is yours. However, we do allow third parties, such as research organizations or archives, to compile public-facing Your Gitea Instance information. Other third parties, such as data brokers, have been known to scrape Your Gitea Instance and compile data as well.</p>
|
||||
|
||||
<p>Your User Personal Information associated with your content could be gathered by third parties in these compilations of Your Gitea Instance data. If you do not want your User Personal Information to appear in third parties’ compilations of Your Gitea Instance data, please do not make your User Personal Information publicly available and be sure to configure your email address to be private in your user profile and in your git commit settings.</p>
|
||||
|
||||
<p>If you would like to compile Your Gitea Instance data, you must comply with our Terms of Service regarding scraping and privacy, and you may only use any public-facing User Personal Information you gather for the purpose for which our user authorized it. For example, where a Your Gitea Instance user has made an email address public-facing for the purpose of identification and attribution, do not use that email address for commercial advertising. We expect you to reasonably secure any User Personal Information you have gathered from Your Gitea Instance, and to respond promptly to complaints, removal requests, and "do not contact" requests from Your Gitea Instance or Your Gitea Instance users.</p>
|
||||
|
||||
<p>In similar fashion, projects on Your Gitea Instance may include publicly available User Personal Information collected as part of the collaborative events.</p>
|
||||
|
||||
<h2>Organizations</h2>
|
||||
|
||||
<p>If you collaborate on or become a member of an Organization, then its Account owners may receive your User Personal Information. When you accept an invitation to an Organization, you will be notified of the types of information owners may be able to see. If you accept an invitation to an Organization with a verified domain, then the owners of that Organization will be able to see your full email address(es) within that Organization's verified domain(s).</p>
|
||||
|
||||
<p>Please note, Your Gitea Instance may share your username, Usage Information, and Device Information with the owner of the Organization you are a member of, to the extent that your User Personal Information is provided only to investigate or respond to a security incident that affects or compromises the security of that particular Organization.</p>
|
||||
|
||||
<p>If you collaborate with or become a member of an Account that has agreed to a Data Protection Addendum (DPA) to this Privacy Policy, then that DPA governs in the event of conflicts between this Privacy Policy and DPA with respect to your activity in the Account.</p>
|
||||
|
||||
<p>Please contact the Account owners for more information about how they might process your User Personal Information in their Organization and the ways for you to access, update, alter, or delete the User Personal Information stored in the Account.</p>
|
||||
|
||||
<h2>How You Can Access and Control the Information We Collect?</h2>
|
||||
|
||||
<p>If you're already a Your Gitea Instance user, you may access, update, alter, or delete your basic user information by editing your user profile. You can control the information we collect about you by limiting what information is in your profile, or by keeping your information current.</p>
|
||||
|
||||
<p>If Your Gitea Instance processes information about you, such as information receives from third parties, and you do not have an account, then you may, subject to applicable law, access, update, alter, delete, or object to the processing of your personal information by contacting our support.</p>
|
||||
|
||||
<h3>Data Portability</h3>
|
||||
|
||||
<p>As a Your Gitea Instance User, you can always take your data with you. You can clone your repositories to your computer, or you can <a href="https://docs.gitea.io/en-us/migrations-interfaces/">perform migrations using the provided interfaces</a>, for example.</p>
|
||||
|
||||
<h3>Data Retention and Deletion of Data</h3>
|
||||
|
||||
<p>In general, Your Gitea Instance retains User Personal Information for as long as your account is active, or as needed to provide you service.</p>
|
||||
|
||||
<p>If you would like to cancel your account or delete your User Personal Information, you may do so in your user profile. We retain and use your information as necessary to comply with our legal obligations, resolve disputes, and enforce our agreements, but barring legal requirements, we will delete your full profile (within reason) within 90 days of your request. Feel free to contact our support to request erasure of the data we process on the bassis of consent within 30 days.</p>
|
||||
|
||||
<p>After an account has been deleted, certain data, such as contributions to other Users' repositories and comments in others' issues, will remain. However, we will delete or de-identify your User Personal Information, including your username and email address, from the author field of issues, pull requests, and comments by associating them with a ghost user.</p>
|
||||
|
||||
<p>That said, the email address you have supplied via your Git commit settings will always be associated with your commits in the Git system. If you choose to make your email address private, you should also update your Git commit settings. We are unable to change or delete data in the Git commit history — the Git software is designed to maintain a record — but we do enable you to control what information you put in that record.</p>
|
||||
|
||||
<h2>Our Global Privacy Practices</h2>
|
||||
|
||||
<p>We store and process the information that we collect in the (country/state where Gitea is deployed) in accordance with this Privacy Statement though our service providers may store and process data outside the (country/state where Gitea is deployed). However, we understand that we have Users from different countries and regions with different privacy expectations, and we try to meet those needs even when the (country/state where Gitea is deployed) does not have the same privacy framework as other countries.</p>
|
||||
|
||||
<p>We provide a high standard of privacy protection—as described in this Privacy Statement—to all our users around the world, regardless of their country of origin or location, and we are proud of the levels of notice, choice, accountability, security, data integrity, access, and recourse we provide. We work hard to comply with the applicable data privacy laws wherever we do business, working with our Data Protection Officer as part of a cross-functional team that oversees our privacy compliance efforts. Additionally, if our vendors or affiliates have access to User Personal Information, they must sign agreements that require them to comply with our privacy policies and with applicable data privacy laws.</p>
|
||||
|
||||
<p>In particular:</p>
|
||||
|
||||
<ul>
|
||||
<li>Your Gitea Instance provides clear methods of unambiguous, informed, specific, and freely given consent at the time of data collection, when we collect your User Personal Information using consent as a basis.</li>
|
||||
<li>We collect only the minimum amount of User Personal Information necessary for our purposes, unless you choose to provide more. We encourage you to only give us the amount of data you are comfortable sharing.</li>
|
||||
<li>We offer you simple methods of accessing, altering, or deleting the User Personal Information we have collected, where legally permitted.</li>
|
||||
<li>We provide our Users notice, choice, accountability, security, and access regarding their User Personal Information, and we limit the purpose for processing it. We also provide our Users a method of recourse and enforcement. These are the Privacy Shield Principles, but they are also just good practices.</li>
|
||||
</ul>
|
||||
|
||||
<h2>How We Communicate with You?</h2>
|
||||
|
||||
<p>We use your email address to communicate with you, if you've said that's okay, and only for the reasons you’ve said that’s okay. For example, if you contact our support with a request, we respond to you via email. You have a lot of control over how your email address is used and shared on and through Your Gitea instance. You may manage your communication preferences in your user profile.</p>
|
||||
|
||||
<p>By design, the Git version control system associates many actions with a User's email address, such as commit messages. We are not able to change many aspects of the Git system. If you would like your email address to remain private, even when you’re commenting on public repositories, you can create a private email address in your user profile. You should also update your local Git configuration to use your private email address. This will not change how we contact you, but it will affect how others see you.</p>
|
||||
|
||||
<p>Depending on your email settings, Your Gitea instance may occasionally send notification emails about changes in a repository you’re watching, new features, requests for feedback, important policy changes, or to offer customer support. We also send marketing emails, based on your choices and in accordance with applicable laws and regulations. There's an “unsubscribe” link located at the bottom of each of the marketing emails we send you. Note that you can opt out of any communications with us, except the important ones (like from our support and system emails).</p>
|
||||
|
||||
<p>Our emails may contain a pixel tag, which is a small, clear image that can tell us whether or not you have opened an email and what your IP address is. We use this pixel tag to make our email more effective for you and to make sure we’re not sending you unwanted email.</p>
|
||||
|
||||
<h2>Changes to this Privacy Policy</h2>
|
||||
|
||||
<p>Although most changes are likely to be minor, Your Gitea Instance may change our Privacy Statement from time to time. We will provide notification to Users of material changes to this Privacy Statement through our Website at least 30 days prior to the change taking effect by posting a notice on our home page or sending email to the primary email address specified in your account.</p>
|
||||
|
||||
<h2>Contact</h2>
|
||||
|
||||
<p>If you have any concerns about privacy, please contact us at <a href="mailto:privacy@your-gitea-instance">privacy@your-gitea-instance</a>. We will respond promptly, within 45 days.</p>
|
||||
|
||||
<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>
|
||||
</body>
|
||||
</html>
|
@@ -1,245 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Terms of Service</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Terms of Service</h1>
|
||||
|
||||
<h4>Last updated: January 29, 2020</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>
|
||||
|
||||
<h2>Definitions</h2>
|
||||
|
||||
<ol>
|
||||
<li>An "Account" represents your legal relationship with Your Gitea Instance. A “User Account” represents an individual User’s authorization to log in to and use the Service and serves as a User’s identity on Your Gitea Instance. “Organizations” are shared workspaces that may be associated with a single entity or with one or more Users where multiple Users can collaborate across many projects at once. A User Account can be a member of any number of Organizations.</li>
|
||||
|
||||
<li>The "Agreement" collectively refers to all terms, conditions, and notices referenced or contained in this document and other operating rules, policies (including Privacy Policy) and procedures that we may publish from time to time on this Website.</li>
|
||||
|
||||
<li>“Content” refers to content featured or displayed through the Website, including without limitation code, text, data, articles, images, photographs, graphics, software, applications, packages, designs, features, and other materials that are available on the Website or otherwise available through the Service. "Content" also includes Services. “User-Generated Content” is Content, written or otherwise, created or uploaded by our Users. "Your Content" is Content that you create or own.</li>
|
||||
|
||||
<li>"Your Gitea Instance", "We", and "Us" refers to Your Gitea Instance, as well as our affiliates, directors, subsidiaries, contractors, licensors, officers, agents, and employees.</li>
|
||||
|
||||
<li>The "Service" refers to applications/software, products, and services provided by Your Gitea Instance.</li>
|
||||
|
||||
<li>The "User", "You", and "Your" refers to individual person or institution (organizations or company) that has visited or using the Service; that have access or use any part of the Account; or that directs to use the Account to perform its function. Please note that additional terms may apply for Accounts related to business or government.</li>
|
||||
|
||||
<li>The "Website" refers to Your Gitea Instance's website at <a href="https://your-gitea-instance">your-gitea-instance</a>, including its subdomains and other websites owned by Your Gitea Instance.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Account Terms</h2>
|
||||
|
||||
<h3>Account Controls</h3>
|
||||
|
||||
<ul>
|
||||
<li>Users: Subject to these Terms, you retain ultimate administrative control over your User Account and the Content within it.</li>
|
||||
|
||||
<li>Organizations. The "owner" of an Organization that was created under these Terms has ultimate administrative control over that Organization and the Content within it. Within the Service, an owner can manage User access to the Organization’s data and projects. An Organization may have multiple owners, but there must be at least one User Account designated as an owner of an Organization. If you are the owner of an Organization under these Terms, we consider you responsible for the actions that are performed on or through that Organization.</li>
|
||||
</ul>
|
||||
|
||||
<h3>Required Information</h3>
|
||||
|
||||
<p>You must provide a valid email address in order to complete the signup process. Any other information requested, such as your real name, is optional, unless you are accepting these terms on behalf of a legal entity (in which case we need more information about the legal entity).</p>
|
||||
|
||||
<h3>Account Requirements</h3>
|
||||
|
||||
<ul>
|
||||
<li>You must be a human to create an Account. Accounts registered by "bots" or other automated methods are not permitted. We do permit machine accounts:</li>
|
||||
<li>A machine account is an Account set up by an individual human who accepts the Terms on behalf of the Account, provides a valid email address, and is responsible for its actions. A machine account is used exclusively for performing automated tasks. Multiple users may direct the actions of a machine account, but the owner of the Account is ultimately responsible for the machine's actions.</li>
|
||||
<li>You must be age 13 or older. If we learn of any User under that age, we will immediately terminate that User's Account. Different countries may have different minimum age; in such cases you are responsible for complying with your country's regulation. By using Your Gitea Instance, you agree to comply with <a href="https://www.ftc.gov/enforcement/rules/rulemaking-regulatory-reform-proceedings/childrens-online-privacy-protection-rule">COPPA</a> and/or similar law in your country.</li>
|
||||
</ul>
|
||||
|
||||
<h3>User Account Security</h3>
|
||||
|
||||
<p>You are responsible for keeping your Account secure while you use our Service. We offer tools such as two-factor authentication to help you maintain your Account's security, but the content of your Account and its security are up to you.</p>
|
||||
|
||||
<h3>Additional Terms</h3>
|
||||
|
||||
<p>In some situations, third parties' terms may apply to your use of Your Gitea Instance. For example, you may be a member of an organization on Your Gitea Instance with its own terms or license agreements; you may download an application that integrates with Your Gitea Instance; or you may use Your Gitea Instance to authenticate to another service. Please be aware that while these Terms are our full agreement with you, other parties' terms govern their relationships with you.</p>
|
||||
|
||||
<h2>Acceptable Use</h2>
|
||||
|
||||
<p>Your use of the Website and Service must not violate any applicable laws, including copyright or trademark laws, export control or sanctions laws, or other laws in your jurisdiction. You are responsible for making sure that your use of the Service is in compliance with laws and any applicable regulations.</p>
|
||||
|
||||
<h2>User-Generated Content</h2>
|
||||
|
||||
<ol>
|
||||
<li>You may create or upload User-Generated Content while using the Service. You are solely responsible for the content of, and for any harm resulting from, any User-Generated Content that you post, upload, link to or otherwise make available via the Service, regardless of the form of that Content. We are not responsible for any public display or misuse of your User-Generated Content.</li>
|
||||
|
||||
<li>We do not pre-screen User-Generated Content, but we have the right (though not the obligation) to refuse or remove any User-Generated Content that, in our sole discretion, violates any our terms or policies.</li>
|
||||
|
||||
<li>
|
||||
<p>You retain ownership of and responsibility for Your Content. If you're posting anything you did not create yourself or do not own the rights to, you agree that you are responsible for any Content you post; that you will only submit Content that you have the right to post; and that you will fully comply with any third party licenses relating to Content you post.</p>
|
||||
|
||||
<p>Because of above, we need you to grant us -- and other Your Gitea Instance users -- certain legal permissions, listed below in this section. If you upload Content that already comes with a license granting Your Gitea Instance the permissions we need to run our Service, no additional license is required. You understand that you will not receive any payment for any of the rights granted below. The licenses you grant to us will end when you remove Your Content from our servers, unless other Users have forked it.</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>We need the legal right to do things like host Your Content, publish it, and share it. You grant us and our legal successors the right to store, parse, and display Your Content, and make incidental copies as necessary to render the Website and provide the Service. This includes the right to do things like copy it to our database and make backups; show it to you and other users; parse it into a search index or otherwise analyze it on our servers; share it with other users; and perform it, in case Your Content is something like music or video.</p>
|
||||
|
||||
<p>This license, however, doesn't grant Your Gitea Instance the right to sell Your Content or otherwise distribute or use it outside of our provision of the Service.</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>Any User-Generated Content you post publicly, including issues, comments, and contributions to other Users' repositories, may be viewed by others. By setting your repositories to be viewed publicly, you agree to allow others to view and "fork" your repositories (this means that others may make their own copies of Content from your repositories in repositories they control).</p>
|
||||
|
||||
<p>If you set your pages and repositories to be viewed publicly, you grant each User of Your Gitea Instance a nonexclusive, worldwide license to use, display, and perform Your Content through the Your Gitea Instance Service and to reproduce Your Content solely on Your Gitea Instance as permitted through Your Gitea Instance's functionality (for example, through forking). You may grant further rights if you adopt a license. If you are uploading Content you did not create or own, you are responsible for ensuring that the Content you upload is licensed under terms that grant these permissions to other Your Gitea Instance Users.</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>Whenever you make a contribution to a repository containing notice of a license, you license your contribution under the same terms, and you agree that you have the right to license your contribution under those terms. If you have a separate agreement to license your contributions under different terms, such as a contributor license agreement, that agreement will supersede.</p>
|
||||
|
||||
<p><i>Isn't this just how it works already? Yep. This is widely accepted as the norm in the open-source community; it's commonly referred to by the shorthand "inbound=outbound". We're just making it explicit.</i></p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>You retain all moral rights to Your Content that you upload, publish, or submit to any part of the Service, including the rights of integrity and attribution. However, you waive these rights and agree not to assert them against us, to enable us to reasonably exercise the rights granted above, but not otherwise.</p>
|
||||
|
||||
<p>To the extent this agreement is not enforceable by applicable law, you grant Your Gitea Instance the rights we need to use Your Content without attribution and to make reasonable adaptations of Your Content as necessary to render the Website and provide the Service.</p>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<h2>Private Repositories</h2>
|
||||
|
||||
<ol>
|
||||
<li>Some Accounts may have private repositories, which allow the User to control access to Content.</li>
|
||||
|
||||
<li>Your Gitea Instance considers the contents of private repositories to be confidential to you. Your Gitea Instance will protect the contents of private repositories from unauthorized use, access, or disclosure in the same manner that we would use to protect our own confidential information of a similar nature and in no event with less than a reasonable degree of care.</li>
|
||||
|
||||
<li>
|
||||
<p>Your Gitea Instance employees may only access the content of your private repositories in the following situations:</p>
|
||||
|
||||
<ul>
|
||||
<li>With your consent and knowledge, for support reasons. If Your Gitea Instance accesses a private repository for support reasons, we will only do so with the owner’s consent and knowledge.</li>
|
||||
<li>When access is required for security reasons, including when access is required to maintain ongoing confidentiality, integrity, availability and resilience of Your Gitea Instance's systems and Service.</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li>You may choose to enable additional access to your private repositories. For example: You may enable various Your Gitea Instance services or features that require additional rights to Your Content in private repositories. These rights may vary depending on the service or feature, but Your Gitea Instance will continue to treat your private repository Content as confidential. If those services or features require rights in addition to those we need to provide the Your Gitea Instance Service, we will provide an explanation of those rights.</li>
|
||||
</ol>
|
||||
|
||||
<h2>Copyright Infringement and DMCA Policy</h2>
|
||||
|
||||
<p>If you are copyright owner and believe that content on our website violates your copyright, please contact us at <a href="mailto:copyright@your-gitea-instance">copyright@your-gitea-instance</a>. Please note that before sending a takedown notice, consider legal uses (such as fair use and licensed use); and legal consequences for sending false notices.</p>
|
||||
|
||||
<h2>Intellectual Properties and COPYING</h2>
|
||||
|
||||
<p>Your Gitea Instance and our licensors, vendors, agents, and/or our content providers retain ownership of all intellectual property rights of any kind related to the Website and Service. We reserve all rights that are not expressly granted to you under this Agreement or by law. The look and feel of the Website and Service is copyright © Your Gitea Instance. All rights reserved.</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>
|
||||
|
||||
<h2>API Terms</h2>
|
||||
|
||||
<p>Abuse or excessively frequent requests to Your Gitea Instance via the API may result in the temporary or permanent suspension of your Account's access to the API. Your Gitea Instance, in our sole discretion, will determine abuse or excessive usage of the API. We will make a reasonable attempt to warn you via email prior to suspension.</p>
|
||||
|
||||
<p>You may not share API tokens to exceed Your Gitea Instance's rate limitations.</p>
|
||||
|
||||
<p>You may not use the API to download data or Content from Your Gitea Instance for spamming purposes, including for the purposes of selling Your Gitea Instance users' personal information, such as to recruiters, headhunters, and job boards.</p>
|
||||
|
||||
<p>All use of the Your Gitea Instance API is subject to these Terms of Service and the Your Gitea Instance Privacy Statement.</p>
|
||||
|
||||
<p>However, we may provide subscription-based access to our API for Users who need high-throughput access or reselling our Service.</p>
|
||||
|
||||
|
||||
<h2>Cancellation and Termination</h2>
|
||||
|
||||
<h3>Account Cancellation</h3>
|
||||
|
||||
<p>It is your responsibility to properly cancel your Account with Your Gitea Instance. You can cancel your Account at any time by going into your Settings in the global navigation bar at the top of the screen. The Account screen provides a simple, no questions asked cancellation link. We are not able to cancel Accounts in response to an email or phone request.</p>
|
||||
|
||||
<h3>Upon Cancellation</h3>
|
||||
|
||||
<p>We will retain and use your information as necessary to comply with our legal obligations, resolve disputes, and enforce our agreements, but barring legal requirements, we will delete your full profile and the Content of your repositories within 90 days of cancellation or termination (though some information may remain in encrypted backups). This information can not be recovered once your Account is cancelled.</p>
|
||||
|
||||
<p>We will not delete Content that you have contributed to other Users' repositories or that other Users have forked.</p>
|
||||
|
||||
<p>Upon request, we will make a reasonable effort to provide an Account owner with a copy of your lawful, non-infringing Account contents after Account cancellation or termination. You must make this request within 90 days of cancellation or termination.</p>
|
||||
|
||||
<h3>We May Terminate</h3>
|
||||
|
||||
<p>Your Gitea Instance has the right to suspend or terminate your access to all or any part of the Website at any time, with or without cause, with or without notice, effective immediately. Your Gitea Instance reserves the right to refuse service to anyone for any reason at any time.</p>
|
||||
|
||||
<h3>Survival</h3>
|
||||
|
||||
<p>All provisions of this Agreement which, by their nature, should survive termination will survive termination — including, without limitation: ownership provisions, warranty disclaimers, indemnity, and limitations of liability.</p>
|
||||
|
||||
<h2>Communications with Us</h2>
|
||||
|
||||
<h3>Electronic Communications Required</h3>
|
||||
|
||||
<p>For contractual purposes, you (1) consent to receive communications from us in an electronic form via the email address you have submitted or via the Service; and (2) agree that all Terms of Service, agreements, notices, disclosures, and other communications that we provide to you electronically satisfy any legal requirement that those communications would satisfy if they were on paper. This section does not affect your non-waivable rights.</p>
|
||||
|
||||
<h3>Legal Notices to Us Must Be in Writing</h3>
|
||||
|
||||
<p>Communications made through email or Your Gitea Instance Support's messaging system will not constitute legal notice to Your Gitea Instance or any of its officers, employees, agents or representatives in any situation where notice to Your Gitea Instance is required by contract or any law or regulation. Legal notice to Your Gitea Instance must be in writing and served on Your Gitea Instance's legal agent.</p>
|
||||
|
||||
<h3>No Phone Support</h3>
|
||||
|
||||
<p>We only offer support via email, in-Service communications, and electronic messages. We do not offer telephone support.</p>
|
||||
|
||||
<h2>Disclaimer of Warranties</h2>
|
||||
|
||||
<p>Your Gitea Instance provides the Website and the Service “as is” and “as available,” without warranty of any kind. Without limiting this, we expressly disclaim all warranties, whether express, implied or statutory, regarding the Website and the Service including without limitation any warranty of merchantability, fitness for a particular purpose, title, security, accuracy and non-infringement.</p>
|
||||
|
||||
<p>Your Gitea Instance does not warrant that the Service will meet your requirements; that the Service will be uninterrupted, timely, secure, or error-free; that the information provided through the Service is accurate, reliable or correct; that any defects or errors will be corrected; that the Service will be available at any particular time or location; or that the Service is free of viruses or other harmful components. You assume full responsibility and risk of loss resulting from your downloading and/or use of files, information, content or other material obtained from the Service.</p>
|
||||
|
||||
<h2>Limitation of Liability</h2>
|
||||
|
||||
<p>You understand and agree that we will not be liable to you or any third party for any loss of profits, use, goodwill, or data, or for any incidental, indirect, special, consequential or exemplary damages, however arising, that result from:</p>
|
||||
|
||||
<ul>
|
||||
<li>the use, disclosure, or display of your User-Generated Content;</li>
|
||||
|
||||
<li>your use or inability to use the Service;</li>
|
||||
|
||||
<li>any modification, price change, suspension or discontinuance of the Service;</li>
|
||||
|
||||
<li>the Service generally or the software or systems that make the Service available;</li>
|
||||
|
||||
<li>unauthorized access to or alterations of your transmissions or data;</li>
|
||||
|
||||
<li>statements or conduct of any third party on the Service;</li>
|
||||
|
||||
<li>any other user interactions that you input or receive through your use of the Service; or
|
||||
any other matter relating to the Service.</li>
|
||||
</ul>
|
||||
|
||||
<p>Our liability is limited whether or not we have been informed of the possibility of such damages, and even if a remedy set forth in this Agreement is found to have failed of its essential purpose. We will have no liability for any failure or delay due to matters beyond our reasonable control.</p>
|
||||
|
||||
<h2>Release and Indemnification</h2>
|
||||
|
||||
<p>If you have a dispute with one or more Users, you agree to release Your Gitea Instance from any and all claims, demands and damages (actual and consequential) of every kind and nature, known and unknown, arising out of or in any way connected with such disputes.</p>
|
||||
|
||||
<p>You agree to indemnify us, defend us, and hold us harmless from and against any and all claims, liabilities, and expenses, including attorneys’ fees, arising out of your use of the Website and the Service, including but not limited to your violation of this Agreement, provided that Your Gitea Instance (1) promptly gives you written notice of the claim, demand, suit or proceeding; (2) gives you sole control of the defense and settlement of the claim, demand, suit or proceeding (provided that you may not settle any claim, demand, suit or proceeding unless the settlement unconditionally releases Your Gitea Instance of all liability); and (3) provides to you all reasonable assistance, at your expense.</p>
|
||||
|
||||
<h2>Changes to These Terms</h2>
|
||||
|
||||
<p>We reserve the right, at our sole discretion, to amend these Terms of Service at any time and will update these Terms of Service in the event of any such amendments. We will notify our Users of material changes to this Agreement, such as price changes, at least 30 days prior to the change taking effect by posting a notice on our Website. For non-material modifications, your continued use of the Website constitutes agreement to our revisions of these Terms of Service.</p>
|
||||
|
||||
<h2>Miscellaneous</h2>
|
||||
|
||||
<h3>Governing Law</h3>
|
||||
|
||||
<p>Except to the extent applicable law provides otherwise, this Agreement between you and us and any access to or use of the Website or the Service are governed by (national laws of country/state where Gitea is deployed) and (regional laws of locality where Gitea is deployed), without regard to conflict of law provisions. You and Your Gitea Instance agree to submit to the exclusive jurisdiction and venue of the courts located in (locality where Gitea is deployed).</p>
|
||||
|
||||
<h3>Non-Assignability</h3>
|
||||
|
||||
<p>Your Gitea Instance may assign or delegate these Terms of Service and/or our Privacy Policy in whole or in part, to any person or entity at any time with or without your consent, including the license granted in <i>User-Generated Content</i>. You may not assign or delegate any rights or obligations under the Terms of Service or Privacy Statement without our prior written consent, and any unauthorized assignment and delegation by you is void.</p>
|
||||
|
||||
<h3>Severablity, No Waiver, and Survival</h3>
|
||||
|
||||
<p>If any part of this Agreement is held invalid or unenforceable, that portion of the Agreement will be construed to reflect the parties’ original intent. The remaining portions will remain in full force and effect. Any failure on the part of Your Gitea Instance to enforce any provision of this Agreement will not be considered a waiver of our right to enforce such provision. Our rights under this Agreement will survive any termination of this Agreement.</p>
|
||||
|
||||
<h3>Amendments and Complete Agreement</h3>
|
||||
|
||||
<p>This Agreement may only be modified by a written amendment signed by an authorized representative of Your Gitea Instance, or by the posting by Your Gitea Instance of a revised version in accordance with <i>Changes to These Terms</i>. These Terms of Service, together with the Your Gitea Instance Privacy Policy, represent the complete and exclusive statement of the agreement between you and us. This Agreement supersedes any proposal or prior agreement oral or written, and any other communications between you and Your Gitea Instance relating to the subject matter of these terms including any confidentiality or nondisclosure agreements.</p>
|
||||
|
||||
<h3>Contact</h3>
|
||||
|
||||
<p>If you have questions about these Terms of Service, you can <a href="mailto:support@your-gitea-instance">contact our support</a>.</p>
|
||||
</body>
|
||||
</html>
|
@@ -1,26 +0,0 @@
|
||||
#424242 Status: Needs feedback ; Feedback is needed
|
||||
#fbc02d Status: In progress ; Work is in progress
|
||||
#43a047 Status: Completed ; Work is complete
|
||||
#00796b Status: Help wanted ;
|
||||
#880e4f Status: Blocked ;
|
||||
#b0bec5 Status: Stale ;
|
||||
|
||||
#d32f2f Kind: Bug ;
|
||||
#607d8b Kind: Question ;
|
||||
#9c27b0 Kind: Security ;
|
||||
#795548 Kind: Testing ;
|
||||
#c62828 Kind: Breaking ;
|
||||
#37474f Kind: Documentation ;
|
||||
#0288d1 Kind: Feature ;
|
||||
#2e7d32 Kind: Enhancement ;
|
||||
#f4511e Kind: Maintenance ;
|
||||
|
||||
#546e7a Reviewed: Invalid ; Something was marked as invalid
|
||||
#616161 Reviewed: Duplicate; Something exists already
|
||||
#795548 Reviewed: Confirmed ; Something has been confirmed
|
||||
#eeeeee Reviewed: Wontfix ; Something won't be fixed
|
||||
|
||||
#d32f2f Priority: High ; The priority is high
|
||||
#e64a19 Priority: Medium ; The priority is medium
|
||||
#4caf50 Priority: Low ; The priority is low
|
||||
#b71c1c Priority: Critical ; The priority is critical
|
@@ -1,7 +1,3 @@
|
||||
// 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 main
|
||||
|
||||
/*
|
||||
@@ -9,7 +5,6 @@ Checkout a PR and load the tests data into sqlite database
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
@@ -25,20 +20,20 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/external"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"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/config"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/xorm"
|
||||
context2 "github.com/gorilla/context"
|
||||
"github.com/unknwon/com"
|
||||
"xorm.io/xorm"
|
||||
"gopkg.in/src-d/go-git.v4"
|
||||
"gopkg.in/src-d/go-git.v4/config"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"gopkg.in/testfixtures.v2"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
var codeFilePath = "contrib/pr/checkout.go"
|
||||
@@ -49,7 +44,6 @@ func runPR() {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
setting.SetCustomPathAndConf("", "", "")
|
||||
setting.NewContext()
|
||||
|
||||
setting.RepoRootPath, err = ioutil.TempDir(os.TempDir(), "repos")
|
||||
@@ -84,24 +78,27 @@ func runPR() {
|
||||
setting.CheckLFSVersion()
|
||||
//models.LoadConfigs()
|
||||
/*
|
||||
setting.Database.Type = "sqlite3"
|
||||
setting.Database.Path = ":memory:"
|
||||
setting.Database.Timeout = 500
|
||||
models.DbCfg.Type = "sqlite3"
|
||||
models.DbCfg.Path = ":memory:"
|
||||
models.DbCfg.Timeout = 500
|
||||
*/
|
||||
db := setting.Cfg.Section("database")
|
||||
db.NewKey("DB_TYPE", "sqlite3")
|
||||
db.NewKey("PATH", ":memory:")
|
||||
|
||||
setting.LogSQL = true
|
||||
models.LoadConfigs()
|
||||
routers.NewServices()
|
||||
setting.Database.LogSQL = true
|
||||
//x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
|
||||
|
||||
models.NewEngine(context.Background(), func(_ *xorm.Engine) error {
|
||||
var helper testfixtures.Helper
|
||||
helper = &testfixtures.SQLite{}
|
||||
models.NewEngine(func(_ *xorm.Engine) error {
|
||||
return nil
|
||||
})
|
||||
models.HasEngine = true
|
||||
//x.ShowSQL(true)
|
||||
err = models.InitFixtures(
|
||||
helper,
|
||||
path.Join(curDir, "models/fixtures/"),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -109,8 +106,9 @@ func runPR() {
|
||||
os.Exit(1)
|
||||
}
|
||||
models.LoadFixtures()
|
||||
util.RemoveAll(setting.RepoRootPath)
|
||||
util.RemoveAll(models.LocalCopyPath())
|
||||
os.RemoveAll(setting.RepoRootPath)
|
||||
os.RemoveAll(models.LocalCopyPath())
|
||||
os.RemoveAll(models.LocalWikiPath())
|
||||
com.CopyDir(path.Join(curDir, "integrations/gitea-repositories-meta"), setting.RepoRootPath)
|
||||
|
||||
log.Printf("[PR] Setting up router\n")
|
||||
@@ -142,20 +140,20 @@ func runPR() {
|
||||
|
||||
log.Printf("[PR] Cleaning up ...\n")
|
||||
/*
|
||||
if err = util.RemoveAll(setting.Indexer.IssuePath); err != nil {
|
||||
fmt.Printf("util.RemoveAll: %v\n", err)
|
||||
if err = os.RemoveAll(setting.Indexer.IssuePath); err != nil {
|
||||
fmt.Printf("os.RemoveAll: %v\n", err)
|
||||
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)
|
||||
os.Exit(1)
|
||||
}
|
||||
*/
|
||||
if err = util.RemoveAll(setting.RepoRootPath); err != nil {
|
||||
log.Fatalf("util.RemoveAll: %v\n", err)
|
||||
if err = os.RemoveAll(setting.RepoRootPath); err != nil {
|
||||
log.Fatalf("os.RemoveAll: %v\n", err)
|
||||
}
|
||||
if err = util.RemoveAll(setting.AppDataPath); err != nil {
|
||||
log.Fatalf("util.RemoveAll: %v\n", err)
|
||||
if err = os.RemoveAll(setting.AppDataPath); err != nil {
|
||||
log.Fatalf("os.RemoveAll: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,9 +197,7 @@ func main() {
|
||||
}
|
||||
remoteUpstream := "origin" //Default
|
||||
for _, r := range remotes {
|
||||
if r.Config().URLs[0] == "https://github.com/go-gitea/gitea.git" ||
|
||||
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
|
||||
if r.Config().URLs[0] == "https://github.com/go-gitea/gitea" || r.Config().URLs[0] == "git@github.com:go-gitea/gitea.git" { //fetch at index 0
|
||||
remoteUpstream = r.Config().Name
|
||||
break
|
||||
}
|
||||
@@ -251,10 +247,9 @@ func main() {
|
||||
log.Fatalf("Failed to duplicate this code file in PR : %v", err)
|
||||
}
|
||||
}
|
||||
//Force build of js, css, bin, ...
|
||||
runCmd("make", "build")
|
||||
time.Sleep(5 * time.Second)
|
||||
//Start with integration test
|
||||
runCmd("go", "run", "-mod", "vendor", "-tags", "sqlite sqlite_unlock_notify", codeFilePath, "-run")
|
||||
runCmd("go", "run", "-tags", "sqlite sqlite_unlock_notify", codeFilePath, "-run")
|
||||
}
|
||||
func runCmd(cmd ...string) {
|
||||
log.Printf("Executing : %s ...\n", cmd)
|
||||
|
@@ -2,42 +2,11 @@
|
||||
Description=Gitea (Git with a cup of tea)
|
||||
After=syslog.target
|
||||
After=network.target
|
||||
###
|
||||
# Don't forget to add the database service requirements
|
||||
###
|
||||
#
|
||||
#Requires=mysql.service
|
||||
#Requires=mariadb.service
|
||||
#Requires=postgresql.service
|
||||
#Requires=memcached.service
|
||||
#Requires=redis.service
|
||||
#
|
||||
###
|
||||
# If using socket activation for main http/s
|
||||
###
|
||||
#
|
||||
#After=gitea.main.socket
|
||||
#Requires=gitea.main.socket
|
||||
#
|
||||
###
|
||||
# (You can also provide gitea an http fallback and/or ssh socket too)
|
||||
#
|
||||
# An example of /etc/systemd/system/gitea.main.socket
|
||||
###
|
||||
##
|
||||
## [Unit]
|
||||
## Description=Gitea Web Socket
|
||||
## PartOf=gitea.service
|
||||
##
|
||||
## [Socket]
|
||||
## Service=gitea.service
|
||||
## ListenStream=<some_port>
|
||||
## NoDelay=true
|
||||
##
|
||||
## [Install]
|
||||
## WantedBy=sockets.target
|
||||
##
|
||||
###
|
||||
|
||||
[Service]
|
||||
# Modify these two values and uncomment them if you have
|
||||
@@ -51,18 +20,14 @@ Type=simple
|
||||
User=git
|
||||
Group=git
|
||||
WorkingDirectory=/var/lib/gitea/
|
||||
# If using Unix socket: tells systemd to create the /run/gitea folder, which will contain the gitea.sock file
|
||||
# (manually creating /run/gitea doesn't work, because it would not persist across reboots)
|
||||
#RuntimeDirectory=gitea
|
||||
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
|
||||
ExecStart=/usr/local/bin/gitea web -c /etc/gitea/app.ini
|
||||
Restart=always
|
||||
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
|
||||
# If you want to bind Gitea to a port below 1024, uncomment
|
||||
# the two values below, or use socket activation to pass Gitea its ports as above
|
||||
# If you want to bind Gitea to a port below 1024 uncomment
|
||||
# the two values below
|
||||
###
|
||||
#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
#AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
###
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
File diff suppressed because it is too large
Load Diff
730
custom/conf/app.ini.sample
Normal file
730
custom/conf/app.ini.sample
Normal file
@@ -0,0 +1,730 @@
|
||||
; This file lists the default values used by Gitea
|
||||
; Copy required sections to your own app.ini (default is custom/conf/app.ini)
|
||||
; and modify as needed.
|
||||
|
||||
; see https://docs.gitea.io/en-us/config-cheat-sheet/ for additional documentation.
|
||||
|
||||
; App name that shows in every page title
|
||||
APP_NAME = Gitea: Git with a cup of tea
|
||||
; Change it if you run locally
|
||||
RUN_USER = git
|
||||
; Either "dev", "prod" or "test", default is "dev"
|
||||
RUN_MODE = dev
|
||||
|
||||
[repository]
|
||||
ROOT =
|
||||
SCRIPT_TYPE = bash
|
||||
; Default ANSI charset
|
||||
ANSI_CHARSET =
|
||||
; Force every new repository to be private
|
||||
FORCE_PRIVATE = false
|
||||
; Default privacy setting when creating a new repository, allowed values: last, private, public. Default is last which means the last setting used.
|
||||
DEFAULT_PRIVATE = last
|
||||
; Global limit of repositories per user, applied at creation time. -1 means no limit
|
||||
MAX_CREATION_LIMIT = -1
|
||||
; Mirror sync queue length, increase if mirror syncing starts hanging
|
||||
MIRROR_QUEUE_LENGTH = 1000
|
||||
; Patch test queue length, increase if pull request patch testing starts hanging
|
||||
PULL_REQUEST_QUEUE_LENGTH = 1000
|
||||
; Preferred Licenses to place at the top of the List
|
||||
; The name here must match the filename in conf/license or custom/conf/license
|
||||
PREFERRED_LICENSES = Apache License 2.0,MIT License
|
||||
; Disable the ability to interact with repositories using the HTTP protocol
|
||||
DISABLE_HTTP_GIT = false
|
||||
; Value for Access-Control-Allow-Origin header, default is not to present
|
||||
; WARNING: This maybe harmful to you website if you do not give it a right value.
|
||||
ACCESS_CONTROL_ALLOW_ORIGIN =
|
||||
; Force ssh:// clone url instead of scp-style uri when default SSH port is used
|
||||
USE_COMPAT_SSH_URI = false
|
||||
; Close issues as long as a commit on any branch marks it as fixed
|
||||
DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH = false
|
||||
|
||||
[repository.editor]
|
||||
; List of file extensions for which lines should be wrapped in the CodeMirror editor
|
||||
; Separate extensions with a comma. To line wrap files without an extension, just put a comma
|
||||
LINE_WRAP_EXTENSIONS = .txt,.md,.markdown,.mdown,.mkd,
|
||||
; Valid file modes that have a preview API associated with them, such as api/v1/markdown
|
||||
; Separate the values by commas. The preview tab in edit mode won't be displayed if the file extension doesn't match
|
||||
PREVIEWABLE_FILE_MODES = markdown
|
||||
|
||||
[repository.local]
|
||||
; Path for local repository copy. Defaults to `tmp/local-repo`
|
||||
LOCAL_COPY_PATH = tmp/local-repo
|
||||
; Path for local wiki copy. Defaults to `tmp/local-wiki`
|
||||
LOCAL_WIKI_PATH = tmp/local-wiki
|
||||
|
||||
[repository.upload]
|
||||
; Whether repository file uploads are enabled. Defaults to `true`
|
||||
ENABLED = true
|
||||
; Path for uploads. Defaults to `data/tmp/uploads` (tmp gets deleted on gitea restart)
|
||||
TEMP_PATH = data/tmp/uploads
|
||||
; One or more allowed types, e.g. image/jpeg|image/png. Nothing means any file type
|
||||
ALLOWED_TYPES =
|
||||
; Max size of each file in megabytes. Defaults to 3MB
|
||||
FILE_MAX_SIZE = 3
|
||||
; Max number of files per upload. Defaults to 5
|
||||
MAX_FILES = 5
|
||||
|
||||
[repository.pull-request]
|
||||
; List of prefixes used in Pull Request title to mark them as Work In Progress
|
||||
WORK_IN_PROGRESS_PREFIXES=WIP:,[WIP]
|
||||
|
||||
[repository.issue]
|
||||
; List of reasons why a Pull Request or Issue can be locked
|
||||
LOCK_REASONS=Too heated,Off-topic,Resolved,Spam
|
||||
|
||||
[ui]
|
||||
; Number of repositories that are displayed on one explore page
|
||||
EXPLORE_PAGING_NUM = 20
|
||||
; Number of issues that are displayed on one page
|
||||
ISSUE_PAGING_NUM = 10
|
||||
; Number of maximum commits displayed in one activity feed
|
||||
FEED_MAX_COMMIT_NUM = 5
|
||||
; Number of maximum commits displayed in commit graph.
|
||||
GRAPH_MAX_COMMIT_NUM = 100
|
||||
; Number of line of codes shown for a code comment
|
||||
CODE_COMMENT_LINES = 4
|
||||
; Value of `theme-color` meta tag, used by Android >= 5.0
|
||||
; An invalid color like "none" or "disable" will have the default style
|
||||
; More info: https://developers.google.com/web/updates/2014/11/Support-for-theme-color-in-Chrome-39-for-Android
|
||||
THEME_COLOR_META_TAG = `#6cc644`
|
||||
; Max size of files to be displayed (default is 8MiB)
|
||||
MAX_DISPLAY_FILE_SIZE = 8388608
|
||||
; Whether the email of the user should be shown in the Explore Users page
|
||||
SHOW_USER_EMAIL = true
|
||||
; Set the default theme for the Gitea install
|
||||
DEFAULT_THEME = gitea
|
||||
; All available themes. Allow users select personalized themes regardless of the value of `DEFAULT_THEME`.
|
||||
THEMES = gitea,arc-green
|
||||
|
||||
[ui.admin]
|
||||
; Number of users that are displayed on one page
|
||||
USER_PAGING_NUM = 50
|
||||
; Number of repos that are displayed on one page
|
||||
REPO_PAGING_NUM = 50
|
||||
; Number of notices that are displayed on one page
|
||||
NOTICE_PAGING_NUM = 25
|
||||
; Number of organizations that are displayed on one page
|
||||
ORG_PAGING_NUM = 50
|
||||
|
||||
[ui.user]
|
||||
; Number of repos that are displayed on one page
|
||||
REPO_PAGING_NUM = 15
|
||||
|
||||
[ui.meta]
|
||||
AUTHOR = Gitea - Git with a cup of tea
|
||||
DESCRIPTION = Gitea (Git with a cup of tea) is a painless self-hosted Git service written in Go
|
||||
KEYWORDS = go,git,self-hosted,gitea
|
||||
|
||||
[markdown]
|
||||
; Enable hard line break extension
|
||||
ENABLE_HARD_LINE_BREAK = false
|
||||
; List of custom URL-Schemes that are allowed as links when rendering Markdown
|
||||
; for example git,magnet
|
||||
CUSTOM_URL_SCHEMES =
|
||||
; List of file extensions that should be rendered/edited as Markdown
|
||||
; Separate the extensions with a comma. To render files without any extension as markdown, just put a comma
|
||||
FILE_EXTENSIONS = .md,.markdown,.mdown,.mkd
|
||||
|
||||
[server]
|
||||
; The protocol the server listens on. One of 'http', 'https', 'unix' or 'fcgi'.
|
||||
PROTOCOL = http
|
||||
DOMAIN = localhost
|
||||
ROOT_URL = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
|
||||
; The address to listen on. Either a IPv4/IPv6 address or the path to a unix socket.
|
||||
HTTP_ADDR = 0.0.0.0
|
||||
HTTP_PORT = 3000
|
||||
; If REDIRECT_OTHER_PORT is true, and PROTOCOL is set to https an http server
|
||||
; will be started on PORT_TO_REDIRECT and it will redirect plain, non-secure http requests to the main
|
||||
; ROOT_URL. Defaults are false for REDIRECT_OTHER_PORT and 80 for
|
||||
; PORT_TO_REDIRECT.
|
||||
REDIRECT_OTHER_PORT = false
|
||||
PORT_TO_REDIRECT = 80
|
||||
; Permission for unix socket
|
||||
UNIX_SOCKET_PERMISSION = 666
|
||||
; Local (DMZ) URL for Gitea workers (such as SSH update) accessing web service.
|
||||
; In most cases you do not need to change the default value.
|
||||
; Alter it only if your SSH server node is not the same as HTTP node.
|
||||
; Do not set this variable if PROTOCOL is set to 'unix'.
|
||||
LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
|
||||
; Disable SSH feature when not available
|
||||
DISABLE_SSH = false
|
||||
; Whether to use the builtin SSH server or not.
|
||||
START_SSH_SERVER = false
|
||||
; Username to use for the builtin SSH server. If blank, then it is the value of RUN_USER.
|
||||
BUILTIN_SSH_SERVER_USER =
|
||||
; Domain name to be exposed in clone URL
|
||||
SSH_DOMAIN = %(DOMAIN)s
|
||||
; The network interface the builtin SSH server should listen on
|
||||
SSH_LISTEN_HOST =
|
||||
; Port number to be exposed in clone URL
|
||||
SSH_PORT = 22
|
||||
; The port number the builtin SSH server should listen on
|
||||
SSH_LISTEN_PORT = %(SSH_PORT)s
|
||||
; Root path of SSH directory, default is '~/.ssh', but you have to use '/home/git/.ssh'.
|
||||
SSH_ROOT_PATH =
|
||||
; Gitea will create a authorized_keys file by default when it is not using the internal ssh server
|
||||
; If you intend to use the AuthorizedKeysCommand functionality then you should turn this off.
|
||||
SSH_CREATE_AUTHORIZED_KEYS_FILE = true
|
||||
; For the built-in SSH server, choose the ciphers to support for SSH connections,
|
||||
; for system SSH this setting has no effect
|
||||
SSH_SERVER_CIPHERS = aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, arcfour256, arcfour128
|
||||
; For the built-in SSH server, choose the key exchange algorithms to support for SSH connections,
|
||||
; for system SSH this setting has no effect
|
||||
SSH_SERVER_KEY_EXCHANGES = diffie-hellman-group1-sha1, diffie-hellman-group14-sha1, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, curve25519-sha256@libssh.org
|
||||
; For the built-in SSH server, choose the MACs to support for SSH connections,
|
||||
; for system SSH this setting has no effect
|
||||
SSH_SERVER_MACS = hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1, hmac-sha1-96
|
||||
; Directory to create temporary files in when testing public keys using ssh-keygen,
|
||||
; default is the system temporary directory.
|
||||
SSH_KEY_TEST_PATH =
|
||||
; Path to ssh-keygen, default is 'ssh-keygen' which means the shell is responsible for finding out which one to call.
|
||||
SSH_KEYGEN_PATH = ssh-keygen
|
||||
; Enable SSH Authorized Key Backup when rewriting all keys, default is true
|
||||
SSH_BACKUP_AUTHORIZED_KEYS = true
|
||||
; Enable exposure of SSH clone URL to anonymous visitors, default is false
|
||||
SSH_EXPOSE_ANONYMOUS = false
|
||||
; Indicate whether to check minimum key size with corresponding type
|
||||
MINIMUM_KEY_SIZE_CHECK = false
|
||||
; Disable CDN even in "prod" mode
|
||||
OFFLINE_MODE = false
|
||||
DISABLE_ROUTER_LOG = false
|
||||
; Generate steps:
|
||||
; $ ./gitea cert -ca=true -duration=8760h0m0s -host=myhost.example.com
|
||||
;
|
||||
; Or from a .pfx file exported from the Windows certificate store (do
|
||||
; not forget to export the private key):
|
||||
; $ openssl pkcs12 -in cert.pfx -out cert.pem -nokeys
|
||||
; $ openssl pkcs12 -in cert.pfx -out key.pem -nocerts -nodes
|
||||
CERT_FILE = custom/https/cert.pem
|
||||
KEY_FILE = custom/https/key.pem
|
||||
; Root directory containing templates and static files.
|
||||
; default is the path where Gitea is executed
|
||||
STATIC_ROOT_PATH =
|
||||
; Default path for App data
|
||||
APP_DATA_PATH = data
|
||||
; Application level GZIP support
|
||||
ENABLE_GZIP = false
|
||||
; Application profiling (memory and cpu)
|
||||
; For "web" command it listens on localhost:6060
|
||||
; For "serve" command it dumps to disk at PPROF_DATA_PATH as (cpuprofile|memprofile)_<username>_<temporary id>
|
||||
ENABLE_PPROF = false
|
||||
; PPROF_DATA_PATH, use an absolute path when you start gitea as service
|
||||
PPROF_DATA_PATH = data/tmp/pprof
|
||||
; Landing page, can be "home", "explore", or "organizations"
|
||||
LANDING_PAGE = home
|
||||
; Enables git-lfs support. true or false, default is false.
|
||||
LFS_START_SERVER = false
|
||||
; Where your lfs files reside, default is data/lfs.
|
||||
LFS_CONTENT_PATH = data/lfs
|
||||
; LFS authentication secret, change this yourself
|
||||
LFS_JWT_SECRET =
|
||||
; LFS authentication validity period (in time.Duration), pushes taking longer than this may fail.
|
||||
LFS_HTTP_AUTH_EXPIRY = 20m
|
||||
|
||||
; Define allowed algorithms and their minimum key length (use -1 to disable a type)
|
||||
[ssh.minimum_key_sizes]
|
||||
ED25519 = 256
|
||||
ECDSA = 256
|
||||
RSA = 2048
|
||||
DSA = 1024
|
||||
|
||||
[database]
|
||||
; Either "mysql", "postgres", "mssql" or "sqlite3", it's your choice
|
||||
DB_TYPE = mysql
|
||||
HOST = 127.0.0.1:3306
|
||||
NAME = gitea
|
||||
USER = root
|
||||
; Use PASSWD = `your password` for quoting if you use special characters in the password.
|
||||
PASSWD =
|
||||
; For Postgres, either "disable" (default), "require", or "verify-full"
|
||||
; For MySQL, either "false" (default), "true", or "skip-verify"
|
||||
SSL_MODE = disable
|
||||
; For "sqlite3" and "tidb", use an absolute path when you start gitea as service
|
||||
PATH = data/gitea.db
|
||||
; For "sqlite3" only. Query timeout
|
||||
SQLITE_TIMEOUT = 500
|
||||
; For iterate buffer, default is 50
|
||||
ITERATE_BUFFER_SIZE = 50
|
||||
; Show the database generated SQL
|
||||
LOG_SQL = true
|
||||
; Maximum number of DB Connect retries
|
||||
DB_RETRIES = 10
|
||||
; Backoff time per DB retry (time.Duration)
|
||||
DB_RETRY_BACKOFF = 3s
|
||||
|
||||
[indexer]
|
||||
; Issue indexer type, currently support: bleve or db, default is bleve
|
||||
ISSUE_INDEXER_TYPE = bleve
|
||||
; Issue indexer storage path, available when ISSUE_INDEXER_TYPE is bleve
|
||||
ISSUE_INDEXER_PATH = indexers/issues.bleve
|
||||
; Issue indexer queue, currently support: channel or levelqueue, default is levelqueue
|
||||
ISSUE_INDEXER_QUEUE_TYPE = levelqueue
|
||||
; When ISSUE_INDEXER_QUEUE_TYPE is levelqueue, this will be the queue will be saved path,
|
||||
; default is indexers/issues.queue
|
||||
ISSUE_INDEXER_QUEUE_DIR = indexers/issues.queue
|
||||
; Batch queue number, default is 20
|
||||
ISSUE_INDEXER_QUEUE_BATCH_NUMBER = 20
|
||||
|
||||
; repo indexer by default disabled, since it uses a lot of disk space
|
||||
REPO_INDEXER_ENABLED = false
|
||||
REPO_INDEXER_PATH = indexers/repos.bleve
|
||||
UPDATE_BUFFER_LEN = 20
|
||||
MAX_FILE_SIZE = 1048576
|
||||
|
||||
[admin]
|
||||
; Disallow regular (non-admin) users from creating organizations.
|
||||
DISABLE_REGULAR_ORG_CREATION = false
|
||||
|
||||
[security]
|
||||
; Whether the installer is disabled
|
||||
INSTALL_LOCK = false
|
||||
; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!!
|
||||
SECRET_KEY = !#@FDEWREWR&*(
|
||||
; How long to remember that an user is logged in before requiring relogin (in days)
|
||||
LOGIN_REMEMBER_DAYS = 7
|
||||
COOKIE_USERNAME = gitea_awesome
|
||||
COOKIE_REMEMBER_NAME = gitea_incredible
|
||||
; Reverse proxy authentication header name of user name
|
||||
REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
|
||||
REVERSE_PROXY_AUTHENTICATION_EMAIL = X-WEBAUTH-EMAIL
|
||||
; The minimum password length for new Users
|
||||
MIN_PASSWORD_LENGTH = 6
|
||||
; Set to true to allow users to import local server paths
|
||||
IMPORT_LOCAL_PATHS = false
|
||||
; Set to true to prevent all users (including admin) from creating custom git hooks
|
||||
DISABLE_GIT_HOOKS = false
|
||||
|
||||
[openid]
|
||||
;
|
||||
; OpenID is an open, standard and decentralized authentication protocol.
|
||||
; Your identity is the address of a webpage you provide, which describes
|
||||
; how to prove you are in control of that page.
|
||||
;
|
||||
; For more info: https://en.wikipedia.org/wiki/OpenID
|
||||
;
|
||||
; Current implementation supports OpenID-2.0
|
||||
;
|
||||
; Tested to work providers at the time of writing:
|
||||
; - Any GNUSocial node (your.hostname.tld/username)
|
||||
; - Any SimpleID provider (http://simpleid.koinic.net)
|
||||
; - http://openid.org.cn/
|
||||
; - openid.stackexchange.com
|
||||
; - login.launchpad.net
|
||||
; - <username>.livejournal.com
|
||||
;
|
||||
; Whether to allow signin in via OpenID
|
||||
ENABLE_OPENID_SIGNIN = true
|
||||
; Whether to allow registering via OpenID
|
||||
; Do not include to rely on rhw DISABLE_REGISTRATION setting
|
||||
;ENABLE_OPENID_SIGNUP = true
|
||||
; Allowed URI patterns (POSIX regexp).
|
||||
; Space separated.
|
||||
; Only these would be allowed if non-blank.
|
||||
; Example value: trusted.domain.org trusted.domain.net
|
||||
WHITELISTED_URIS =
|
||||
; Forbidden URI patterns (POSIX regexp).
|
||||
; Space separated.
|
||||
; Only used if WHITELISTED_URIS is blank.
|
||||
; Example value: loadaverage.org/badguy stackexchange.com/.*spammer
|
||||
BLACKLISTED_URIS =
|
||||
|
||||
[service]
|
||||
; Time limit to confirm account/email registration
|
||||
ACTIVE_CODE_LIVE_MINUTES = 180
|
||||
; Time limit to perform the reset of a forgotten password
|
||||
RESET_PASSWD_CODE_LIVE_MINUTES = 180
|
||||
; Whether a new user needs to confirm their email when registering.
|
||||
REGISTER_EMAIL_CONFIRM = false
|
||||
; List of domain names that are allowed to be used to register on a Gitea instance
|
||||
; gitea.io,example.com
|
||||
EMAIL_DOMAIN_WHITELIST=
|
||||
; Disallow registration, only allow admins to create accounts.
|
||||
DISABLE_REGISTRATION = false
|
||||
; Allow registration only using third-party services, it works only when DISABLE_REGISTRATION is false
|
||||
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
|
||||
; User must sign in to view anything.
|
||||
REQUIRE_SIGNIN_VIEW = false
|
||||
; Mail notification
|
||||
ENABLE_NOTIFY_MAIL = false
|
||||
; More detail: https://github.com/gogits/gogs/issues/165
|
||||
ENABLE_REVERSE_PROXY_AUTHENTICATION = false
|
||||
ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false
|
||||
ENABLE_REVERSE_PROXY_EMAIL = false
|
||||
; Enable captcha validation for registration
|
||||
ENABLE_CAPTCHA = false
|
||||
; Type of captcha you want to use. Options: image, recaptcha
|
||||
CAPTCHA_TYPE = image
|
||||
; Enable recaptcha to use Google's recaptcha service
|
||||
; Go to https://www.google.com/recaptcha/admin to sign up for a key
|
||||
RECAPTCHA_SECRET =
|
||||
RECAPTCHA_SITEKEY =
|
||||
; Default value for KeepEmailPrivate
|
||||
; Each new user will get the value of this setting copied into their profile
|
||||
DEFAULT_KEEP_EMAIL_PRIVATE = false
|
||||
; Default value for AllowCreateOrganization
|
||||
; Every new user will have rights set to create organizations depending on this setting
|
||||
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
|
||||
; Either "public", "limited" or "private", default is "public"
|
||||
; Limited is for signed user only
|
||||
; Private is only for member of the organization
|
||||
; Public is for everyone
|
||||
DEFAULT_ORG_VISIBILITY = public
|
||||
; Default value for EnableDependencies
|
||||
; Repositories will use dependencies by default depending on this setting
|
||||
DEFAULT_ENABLE_DEPENDENCIES = true
|
||||
; Enable heatmap on users profiles.
|
||||
ENABLE_USER_HEATMAP = true
|
||||
; Enable Timetracking
|
||||
ENABLE_TIMETRACKING = true
|
||||
; Default value for EnableTimetracking
|
||||
; Repositories will use timetracking by default depending on this setting
|
||||
DEFAULT_ENABLE_TIMETRACKING = true
|
||||
; Default value for AllowOnlyContributorsToTrackTime
|
||||
; Only users with write permissions can track time if this is true
|
||||
DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME = true
|
||||
; Default value for the domain part of the user's email address in the git log
|
||||
; if he has set KeepEmailPrivate to true. The user's email will be replaced with a
|
||||
; concatenation of the user name in lower case, "@" and NO_REPLY_ADDRESS.
|
||||
NO_REPLY_ADDRESS = noreply.example.org
|
||||
; Show Registration button
|
||||
SHOW_REGISTRATION_BUTTON = true
|
||||
; Default value for AutoWatchNewRepos
|
||||
; When adding a repo to a team or creating a new repo all team members will watch the
|
||||
; repo automatically if enabled
|
||||
AUTO_WATCH_NEW_REPOS = true
|
||||
|
||||
[webhook]
|
||||
; Hook task queue length, increase if webhook shooting starts hanging
|
||||
QUEUE_LENGTH = 1000
|
||||
; Deliver timeout in seconds
|
||||
DELIVER_TIMEOUT = 5
|
||||
; Allow insecure certification
|
||||
SKIP_TLS_VERIFY = false
|
||||
; Number of history information in each page
|
||||
PAGING_NUM = 10
|
||||
|
||||
[mailer]
|
||||
ENABLED = false
|
||||
; Buffer length of channel, keep it as it is if you don't know what it is.
|
||||
SEND_BUFFER_LEN = 100
|
||||
; Name displayed in mail title
|
||||
SUBJECT = %(APP_NAME)s
|
||||
; Mail server
|
||||
; Gmail: smtp.gmail.com:587
|
||||
; QQ: smtp.qq.com:465
|
||||
; Note, if the port ends with "465", SMTPS will be used. Using STARTTLS on port 587 is recommended per RFC 6409. If the server supports STARTTLS it will always be used.
|
||||
HOST =
|
||||
; Disable HELO operation when hostnames are different.
|
||||
DISABLE_HELO =
|
||||
; Custom hostname for HELO operation, if no value is provided, one is retrieved from system.
|
||||
HELO_HOSTNAME =
|
||||
; Do not verify the certificate of the server. Only use this for self-signed certificates
|
||||
SKIP_VERIFY =
|
||||
; Use client certificate
|
||||
USE_CERTIFICATE = false
|
||||
CERT_FILE = custom/mailer/cert.pem
|
||||
KEY_FILE = custom/mailer/key.pem
|
||||
; Should SMTP connection use TLS
|
||||
IS_TLS_ENABLED = false
|
||||
; Mail from address, RFC 5322. This can be just an email address, or the `"Name" <email@example.com>` format
|
||||
FROM =
|
||||
; Mailer user name and password
|
||||
USER =
|
||||
; Use PASSWD = `your password` for quoting if you use special characters in the password.
|
||||
PASSWD =
|
||||
; Send mails as plain text
|
||||
SEND_AS_PLAIN_TEXT = false
|
||||
; Set Mailer Type (either SMTP, sendmail or dummy to just send to the log)
|
||||
MAILER_TYPE = smtp
|
||||
; Specify an alternative sendmail binary
|
||||
SENDMAIL_PATH = sendmail
|
||||
; Specify any extra sendmail arguments
|
||||
SENDMAIL_ARGS =
|
||||
|
||||
[cache]
|
||||
; Either "memory", "redis", or "memcache", default is "memory"
|
||||
ADAPTER = memory
|
||||
; For "memory" only, GC interval in seconds, default is 60
|
||||
INTERVAL = 60
|
||||
; For "redis" and "memcache", connection host address
|
||||
; redis: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180
|
||||
; memcache: `127.0.0.1:11211`
|
||||
HOST =
|
||||
; Time to keep items in cache if not used, default is 16 hours.
|
||||
; Setting it to 0 disables caching
|
||||
ITEM_TTL = 16h
|
||||
|
||||
[session]
|
||||
; Either "memory", "file", or "redis", default is "memory"
|
||||
PROVIDER = memory
|
||||
; Provider config options
|
||||
; memory: doesn't have any config yet
|
||||
; file: session file path, e.g. `data/sessions`
|
||||
; redis: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180
|
||||
; mysql: go-sql-driver/mysql dsn config string, e.g. `root:password@/session_table`
|
||||
PROVIDER_CONFIG = data/sessions
|
||||
; Session cookie name
|
||||
COOKIE_NAME = i_like_gitea
|
||||
; If you use session in https only, default is false
|
||||
COOKIE_SECURE = false
|
||||
; Enable set cookie, default is true
|
||||
ENABLE_SET_COOKIE = true
|
||||
; Session GC time interval in seconds, default is 86400 (1 day)
|
||||
GC_INTERVAL_TIME = 86400
|
||||
; Session life time in seconds, default is 86400 (1 day)
|
||||
SESSION_LIFE_TIME = 86400
|
||||
|
||||
[picture]
|
||||
AVATAR_UPLOAD_PATH = data/avatars
|
||||
; Max Width and Height of uploaded avatars. This is to limit the amount of RAM
|
||||
; used when resizing the image.
|
||||
AVATAR_MAX_WIDTH = 4096
|
||||
AVATAR_MAX_HEIGHT = 3072
|
||||
; Chinese users can choose "duoshuo"
|
||||
; or a custom avatar source, like: http://cn.gravatar.com/avatar/
|
||||
GRAVATAR_SOURCE = gravatar
|
||||
; This value will always be true in offline mode.
|
||||
DISABLE_GRAVATAR = false
|
||||
; Federated avatar lookup uses DNS to discover avatar associated
|
||||
; with emails, see https://www.libravatar.org
|
||||
; This value will always be false in offline mode or when Gravatar is disabled.
|
||||
ENABLE_FEDERATED_AVATAR = false
|
||||
|
||||
[attachment]
|
||||
; Whether attachments are enabled. Defaults to `true`
|
||||
ENABLED = true
|
||||
; Path for attachments. Defaults to `data/attachments`
|
||||
PATH = data/attachments
|
||||
; One or more allowed types, e.g. image/jpeg|image/png
|
||||
ALLOWED_TYPES = image/jpeg|image/png|application/zip|application/gzip
|
||||
; Max size of each file. Defaults to 4MB
|
||||
MAX_SIZE = 4
|
||||
; Max number of files per upload. Defaults to 5
|
||||
MAX_FILES = 5
|
||||
|
||||
[time]
|
||||
; Specifies the format for fully outputted dates. Defaults to RFC1123
|
||||
; Special supported values are ANSIC, UnixDate, RubyDate, RFC822, RFC822Z, RFC850, RFC1123, RFC1123Z, RFC3339, RFC3339Nano, Kitchen, Stamp, StampMilli, StampMicro and StampNano
|
||||
; For more information about the format see http://golang.org/pkg/time/#pkg-constants
|
||||
FORMAT =
|
||||
|
||||
[log]
|
||||
ROOT_PATH =
|
||||
; Either "console", "file", "conn", "smtp" or "database", default is "console"
|
||||
; Use comma to separate multiple modes, e.g. "console, file"
|
||||
MODE = console
|
||||
; Buffer length of the channel, keep it as it is if you don't know what it is.
|
||||
BUFFER_LEN = 10000
|
||||
; Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace"
|
||||
LEVEL = Trace
|
||||
REDIRECT_MACARON_LOG = false
|
||||
|
||||
; For "console" mode only
|
||||
[log.console]
|
||||
LEVEL =
|
||||
|
||||
; For "file" mode only
|
||||
[log.file]
|
||||
LEVEL =
|
||||
; This enables automated log rotate(switch of following options), default is true
|
||||
LOG_ROTATE = true
|
||||
; Max number of lines in a single file, default is 1000000
|
||||
MAX_LINES = 1000000
|
||||
; Max size shift of a single file, default is 28 means 1 << 28, 256MB
|
||||
MAX_SIZE_SHIFT = 28
|
||||
; Segment log daily, default is true
|
||||
DAILY_ROTATE = true
|
||||
; delete the log file after n days, default is 7
|
||||
MAX_DAYS = 7
|
||||
|
||||
; For "conn" mode only
|
||||
[log.conn]
|
||||
LEVEL =
|
||||
; Reconnect host for every single message, default is false
|
||||
RECONNECT_ON_MSG = false
|
||||
; Try to reconnect when connection is lost, default is false
|
||||
RECONNECT = false
|
||||
; Either "tcp", "unix" or "udp", default is "tcp"
|
||||
PROTOCOL = tcp
|
||||
; Host address
|
||||
ADDR =
|
||||
|
||||
; For "smtp" mode only
|
||||
[log.smtp]
|
||||
LEVEL =
|
||||
; Name displayed in mail title, default is "Diagnostic message from server"
|
||||
SUBJECT = Diagnostic message from server
|
||||
; Mail server
|
||||
HOST =
|
||||
; Mailer user name and password
|
||||
USER =
|
||||
; Use PASSWD = `your password` for quoting if you use special characters in the password.
|
||||
PASSWD =
|
||||
; Receivers, can be one or more, e.g. 1@example.com,2@example.com
|
||||
RECEIVERS =
|
||||
|
||||
; For "database" mode only
|
||||
[log.database]
|
||||
LEVEL =
|
||||
; Either "mysql" or "postgres"
|
||||
DRIVER =
|
||||
; Based on xorm, e.g.: root:root@localhost/gitea?charset=utf8
|
||||
CONN =
|
||||
|
||||
[cron]
|
||||
; Enable running cron tasks periodically.
|
||||
ENABLED = true
|
||||
; Run cron tasks when Gitea starts.
|
||||
RUN_AT_START = false
|
||||
|
||||
; Update mirrors
|
||||
[cron.update_mirrors]
|
||||
SCHEDULE = @every 10m
|
||||
|
||||
; Repository health check
|
||||
[cron.repo_health_check]
|
||||
SCHEDULE = @every 24h
|
||||
TIMEOUT = 60s
|
||||
; Arguments for command 'git fsck', e.g. "--unreachable --tags"
|
||||
; see more on http://git-scm.com/docs/git-fsck
|
||||
ARGS =
|
||||
|
||||
; Check repository statistics
|
||||
[cron.check_repo_stats]
|
||||
RUN_AT_START = true
|
||||
SCHEDULE = @every 24h
|
||||
|
||||
; Clean up old repository archives
|
||||
[cron.archive_cleanup]
|
||||
; Whether to enable the job
|
||||
ENABLED = true
|
||||
; Whether to always run at least once at start up time (if ENABLED)
|
||||
RUN_AT_START = true
|
||||
; Time interval for job to run
|
||||
SCHEDULE = @every 24h
|
||||
; Archives created more than OLDER_THAN ago are subject to deletion
|
||||
OLDER_THAN = 24h
|
||||
|
||||
; Synchronize external user data (only LDAP user synchronization is supported)
|
||||
[cron.sync_external_users]
|
||||
; Synchronize external user data when starting server (default false)
|
||||
RUN_AT_START = false
|
||||
; Interval as a duration between each synchronization (default every 24h)
|
||||
SCHEDULE = @every 24h
|
||||
; Create new users, update existing user data and disable users that are not in external source anymore (default)
|
||||
; or only create new users if UPDATE_EXISTING is set to false
|
||||
UPDATE_EXISTING = true
|
||||
|
||||
[git]
|
||||
; Disables highlight of added and removed changes
|
||||
DISABLE_DIFF_HIGHLIGHT = false
|
||||
; Max number of lines allowed in a single file in diff view
|
||||
MAX_GIT_DIFF_LINES = 1000
|
||||
; Max number of allowed characters in a line in diff view
|
||||
MAX_GIT_DIFF_LINE_CHARACTERS = 5000
|
||||
; Max number of files shown in diff view
|
||||
MAX_GIT_DIFF_FILES = 100
|
||||
; Arguments for command 'git gc', e.g. "--aggressive --auto"
|
||||
; see more on http://git-scm.com/docs/git-gc/
|
||||
GC_ARGS =
|
||||
|
||||
; Operation timeout in seconds
|
||||
[git.timeout]
|
||||
DEFAULT = 360
|
||||
MIGRATE = 600
|
||||
MIRROR = 300
|
||||
CLONE = 300
|
||||
PULL = 300
|
||||
GC = 60
|
||||
|
||||
[mirror]
|
||||
; Default interval as a duration between each check
|
||||
DEFAULT_INTERVAL = 8h
|
||||
; Min interval as a duration must be > 1m
|
||||
MIN_INTERVAL = 10m
|
||||
|
||||
[api]
|
||||
; Enables Swagger. True or false; default is true.
|
||||
ENABLE_SWAGGER = true
|
||||
; Max number of items in a page
|
||||
MAX_RESPONSE_ITEMS = 50
|
||||
; Default paging number of api
|
||||
DEFAULT_PAGING_NUM = 30
|
||||
; Default and maximum number of items per page for git trees api
|
||||
DEFAULT_GIT_TREES_PER_PAGE = 1000
|
||||
|
||||
[oauth2]
|
||||
; Enables OAuth2 provider
|
||||
ENABLED = true
|
||||
; Lifetime of an OAuth2 access token in seconds
|
||||
ACCESS_TOKEN_EXPIRATION_TIME=3600
|
||||
; Lifetime of an OAuth2 access token in hours
|
||||
REFRESH_TOKEN_EXPIRATION_TIME=730
|
||||
; Check if refresh token got already used
|
||||
INVALIDATE_REFRESH_TOKENS=false
|
||||
; OAuth2 authentication secret for access and refresh tokens, change this a unique string.
|
||||
JWT_SECRET=Bk0yK7Y9g_p56v86KaHqjSbxvNvu3SbKoOdOt2ZcXvU
|
||||
|
||||
[i18n]
|
||||
LANGS = en-US,zh-CN,zh-HK,zh-TW,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,uk-UA,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT,fi-FI,tr-TR,cs-CZ,sr-SP,sv-SE,ko-KR
|
||||
NAMES = English,简体中文,繁體中文(香港),繁體中文(台灣),Deutsch,français,Nederlands,latviešu,русский,Українська,日本語,español,português do Brasil,polski,български,italiano,suomi,Türkçe,čeština,српски,svenska,한국어
|
||||
|
||||
; Used for datetimepicker
|
||||
[i18n.datelang]
|
||||
en-US = en
|
||||
zh-CN = zh
|
||||
zh-HK = zh-HK
|
||||
zh-TW = zh-TW
|
||||
de-DE = de
|
||||
fr-FR = fr
|
||||
nl-NL = nl
|
||||
lv-LV = lv
|
||||
ru-RU = ru
|
||||
uk-UA = uk
|
||||
ja-JP = ja
|
||||
es-ES = es
|
||||
pt-BR = pt-BR
|
||||
pl-PL = pl
|
||||
bg-BG = bg
|
||||
it-IT = it
|
||||
fi-FI = fi
|
||||
tr-TR = tr
|
||||
cs-CZ = cs-CZ
|
||||
sr-SP = sr
|
||||
sv-SE = sv
|
||||
ko-KR = ko
|
||||
|
||||
[U2F]
|
||||
; NOTE: THE DEFAULT VALUES HERE WILL NEED TO BE CHANGED
|
||||
; Two Factor authentication with security keys
|
||||
; https://developers.yubico.com/U2F/App_ID.html
|
||||
;APP_ID = http://localhost:3000/
|
||||
; Comma seperated list of trusted facets
|
||||
;TRUSTED_FACETS = http://localhost:3000/
|
||||
|
||||
; Extension mapping to highlight class
|
||||
; e.g. .toml=ini
|
||||
[highlight.mapping]
|
||||
|
||||
[other]
|
||||
SHOW_FOOTER_BRANDING = false
|
||||
; Show version information about Gitea and Go in the footer
|
||||
SHOW_FOOTER_VERSION = true
|
||||
; Show template execution time in the footer
|
||||
SHOW_FOOTER_TEMPLATE_LOAD_TIME = true
|
||||
|
||||
[markup.asciidoc]
|
||||
ENABLED = false
|
||||
; List of file extensions that should be rendered by an external command
|
||||
FILE_EXTENSIONS = .adoc,.asciidoc
|
||||
; External command to render all matching extensions
|
||||
RENDER_COMMAND = "asciidoc --out-file=- -"
|
||||
; Don't pass the file on STDIN, pass the filename as argument instead.
|
||||
IS_INPUT_FILE = false
|
||||
|
||||
[metrics]
|
||||
; Enables metrics endpoint. True or false; default is false.
|
||||
ENABLED = false
|
||||
; If you want to add authorization, specify a token here
|
||||
TOKEN =
|
15
docker/Makefile
vendored
Normal file
15
docker/Makefile
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
#Makefile related to docker
|
||||
|
||||
DOCKER_IMAGE ?= gitea/gitea
|
||||
DOCKER_TAG ?= latest
|
||||
DOCKER_REF := $(DOCKER_IMAGE):$(DOCKER_TAG)
|
||||
|
||||
|
||||
.PHONY: docker
|
||||
docker:
|
||||
docker build --disable-content-trust=false -t $(DOCKER_REF) .
|
||||
# support also build args docker build --build-arg GITEA_VERSION=v1.2.3 --build-arg TAGS="bindata sqlite sqlite_unlock_notify" .
|
||||
|
||||
.PHONY: docker-build
|
||||
docker-build:
|
||||
docker run -ti --rm -v $(CURDIR):/srv/app/src/code.gitea.io/gitea -w /srv/app/src/code.gitea.io/gitea -e TAGS="bindata $(TAGS)" webhippie/golang:edge make clean generate build
|
7
docker/README.md
vendored
7
docker/README.md
vendored
@@ -1,7 +0,0 @@
|
||||
# Gitea - Docker
|
||||
|
||||
Dockerfile is found in root of repository.
|
||||
|
||||
Docker image can be found on [docker hub](https://hub.docker.com/r/gitea/gitea)
|
||||
|
||||
Documentation on using docker image can be found on [Gitea Docs site](https://docs.gitea.io/en-us/install-with-docker/)
|
2
docker/etc/profile.d/gitea.sh
Executable file
2
docker/etc/profile.d/gitea.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
export GITEA_CUSTOM=/data/gitea
|
46
docker/etc/s6/gitea/setup
Executable file
46
docker/etc/s6/gitea/setup
Executable file
@@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ ! -d /data/git/.ssh ]; then
|
||||
mkdir -p /data/git/.ssh
|
||||
chmod 700 /data/git/.ssh
|
||||
fi
|
||||
|
||||
if [ ! -f /data/git/.ssh/environment ]; then
|
||||
echo "GITEA_CUSTOM=/data/gitea" >| /data/git/.ssh/environment
|
||||
chmod 600 /data/git/.ssh/environment
|
||||
fi
|
||||
|
||||
if [ ! -f /data/gitea/conf/app.ini ]; then
|
||||
mkdir -p /data/gitea/conf
|
||||
|
||||
# Set INSTALL_LOCK to true only if SECRET_KEY is not empty and
|
||||
# INSTALL_LOCK is empty
|
||||
if [ -n "$SECRET_KEY" ] && [ -z "$INSTALL_LOCK" ]; then
|
||||
INSTALL_LOCK=true
|
||||
fi
|
||||
|
||||
# Substitude the environment variables in the template
|
||||
APP_NAME=${APP_NAME:-"Gitea: Git with a cup of tea"} \
|
||||
RUN_MODE=${RUN_MODE:-"dev"} \
|
||||
SSH_DOMAIN=${SSH_DOMAIN:-"localhost"} \
|
||||
HTTP_PORT=${HTTP_PORT:-"3000"} \
|
||||
ROOT_URL=${ROOT_URL:-""} \
|
||||
DISABLE_SSH=${DISABLE_SSH:-"false"} \
|
||||
SSH_PORT=${SSH_PORT:-"22"} \
|
||||
DB_TYPE=${DB_TYPE:-"sqlite3"} \
|
||||
DB_HOST=${DB_HOST:-"localhost:3306"} \
|
||||
DB_NAME=${DB_NAME:-"gitea"} \
|
||||
DB_USER=${DB_USER:-"root"} \
|
||||
DB_PASSWD=${DB_PASSWD:-""} \
|
||||
INSTALL_LOCK=${INSTALL_LOCK:-"false"} \
|
||||
DISABLE_REGISTRATION=${DISABLE_REGISTRATION:-"false"} \
|
||||
REQUIRE_SIGNIN_VIEW=${REQUIRE_SIGNIN_VIEW:-"false"} \
|
||||
SECRET_KEY=${SECRET_KEY:-""} \
|
||||
envsubst < /etc/templates/app.ini > /data/gitea/conf/app.ini
|
||||
fi
|
||||
|
||||
# only chown if current owner is not already the gitea ${USER}. No recursive check to save time
|
||||
if ! [[ $(ls -ld /data/gitea | awk '{print $3}') = ${USER} ]]; then chown -R ${USER}:git /data/gitea; fi
|
||||
if ! [[ $(ls -ld /app/gitea | awk '{print $3}') = ${USER} ]]; then chown -R ${USER}:git /app/gitea; fi
|
||||
if ! [[ $(ls -ld /data/git | awk '{print $3}') = ${USER} ]]; then chown -R ${USER}:git /data/git; fi
|
||||
chmod 0755 /data/gitea /app/gitea /data/git
|
2
docker/etc/s6/openssh/finish
Executable file
2
docker/etc/s6/openssh/finish
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
exit 0
|
6
docker/etc/s6/openssh/run
Executable file
6
docker/etc/s6/openssh/run
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
[[ -f ./setup ]] && source ./setup
|
||||
|
||||
pushd /root > /dev/null
|
||||
exec su-exec root /usr/sbin/sshd -D
|
||||
popd
|
29
docker/etc/s6/openssh/setup
Executable file
29
docker/etc/s6/openssh/setup
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ ! -d /data/ssh ]; then
|
||||
mkdir -p /data/ssh
|
||||
fi
|
||||
|
||||
if [ ! -f /data/ssh/ssh_host_ed25519_key ]; then
|
||||
echo "Generating /data/ssh/ssh_host_ed25519_key..."
|
||||
ssh-keygen -t ed25519 -b 4096 -f /data/ssh/ssh_host_ed25519_key -N "" > /dev/null
|
||||
fi
|
||||
|
||||
if [ ! -f /data/ssh/ssh_host_rsa_key ]; then
|
||||
echo "Generating /data/ssh/ssh_host_rsa_key..."
|
||||
ssh-keygen -t rsa -b 2048 -f /data/ssh/ssh_host_rsa_key -N "" > /dev/null
|
||||
fi
|
||||
|
||||
if [ ! -f /data/ssh/ssh_host_dsa_key ]; then
|
||||
echo "Generating /data/ssh/ssh_host_dsa_key..."
|
||||
ssh-keygen -t dsa -f /data/ssh/ssh_host_dsa_key -N "" > /dev/null
|
||||
fi
|
||||
|
||||
if [ ! -f /data/ssh/ssh_host_ecdsa_key ]; then
|
||||
echo "Generating /data/ssh/ssh_host_ecdsa_key..."
|
||||
ssh-keygen -t ecdsa -b 256 -f /data/ssh/ssh_host_ecdsa_key -N "" > /dev/null
|
||||
fi
|
||||
|
||||
chown root:root /data/ssh/*
|
||||
chmod 0700 /data/ssh
|
||||
chmod 0600 /data/ssh/*
|
2
docker/etc/s6/syslogd/finish
Executable file
2
docker/etc/s6/syslogd/finish
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
exit 0
|
6
docker/etc/s6/syslogd/run
Executable file
6
docker/etc/s6/syslogd/run
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
[[ -f ./setup ]] && source ./setup
|
||||
|
||||
pushd /root > /dev/null
|
||||
exec su-exec root /sbin/syslogd -nS -O-
|
||||
popd
|
1
docker/etc/s6/syslogd/setup
Executable file
1
docker/etc/s6/syslogd/setup
Executable file
@@ -0,0 +1 @@
|
||||
#!/bin/bash
|
33
docker/etc/ssh/sshd_config
Normal file
33
docker/etc/ssh/sshd_config
Normal file
@@ -0,0 +1,33 @@
|
||||
Port 22
|
||||
Protocol 2
|
||||
|
||||
AddressFamily any
|
||||
ListenAddress 0.0.0.0
|
||||
ListenAddress ::
|
||||
|
||||
LogLevel INFO
|
||||
|
||||
HostKey /data/ssh/ssh_host_ed25519_key
|
||||
HostKey /data/ssh/ssh_host_rsa_key
|
||||
HostKey /data/ssh/ssh_host_dsa_key
|
||||
HostKey /data/ssh/ssh_host_ecdsa_key
|
||||
|
||||
AuthorizedKeysFile .ssh/authorized_keys
|
||||
|
||||
UseDNS no
|
||||
AllowAgentForwarding no
|
||||
AllowTcpForwarding no
|
||||
PrintMotd no
|
||||
|
||||
PermitUserEnvironment yes
|
||||
PermitRootLogin no
|
||||
ChallengeResponseAuthentication no
|
||||
PasswordAuthentication no
|
||||
PermitEmptyPasswords no
|
||||
|
||||
AllowUsers git
|
||||
|
||||
Banner none
|
||||
Subsystem sftp /usr/lib/ssh/sftp-server
|
||||
|
||||
AcceptEnv GIT_PROTOCOL
|
51
docker/etc/templates/app.ini
Normal file
51
docker/etc/templates/app.ini
Normal file
@@ -0,0 +1,51 @@
|
||||
APP_NAME = $APP_NAME
|
||||
RUN_MODE = $RUN_MODE
|
||||
|
||||
[repository]
|
||||
ROOT = /data/git/repositories
|
||||
|
||||
[repository.local]
|
||||
LOCAL_COPY_PATH = /data/gitea/tmp/local-repo
|
||||
|
||||
[repository.upload]
|
||||
TEMP_PATH = /data/gitea/uploads
|
||||
|
||||
[server]
|
||||
APP_DATA_PATH = /data/gitea
|
||||
SSH_DOMAIN = $SSH_DOMAIN
|
||||
HTTP_PORT = $HTTP_PORT
|
||||
ROOT_URL = $ROOT_URL
|
||||
DISABLE_SSH = $DISABLE_SSH
|
||||
SSH_PORT = $SSH_PORT
|
||||
LFS_CONTENT_PATH = /data/git/lfs
|
||||
|
||||
[database]
|
||||
PATH = /data/gitea/gitea.db
|
||||
DB_TYPE = $DB_TYPE
|
||||
HOST = $DB_HOST
|
||||
NAME = $DB_NAME
|
||||
USER = $DB_USER
|
||||
PASSWD = $DB_PASSWD
|
||||
|
||||
[indexer]
|
||||
ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve
|
||||
|
||||
[session]
|
||||
PROVIDER_CONFIG = /data/gitea/sessions
|
||||
|
||||
[picture]
|
||||
AVATAR_UPLOAD_PATH = /data/gitea/avatars
|
||||
|
||||
[attachment]
|
||||
PATH = /data/gitea/attachments
|
||||
|
||||
[log]
|
||||
ROOT_PATH = /data/gitea/log
|
||||
|
||||
[security]
|
||||
INSTALL_LOCK = $INSTALL_LOCK
|
||||
SECRET_KEY = $SECRET_KEY
|
||||
|
||||
[service]
|
||||
DISABLE_REGISTRATION = $DISABLE_REGISTRATION
|
||||
REQUIRE_SIGNIN_VIEW = $REQUIRE_SIGNIN_VIEW
|
19
docker/manifest.tmpl
vendored
19
docker/manifest.tmpl
vendored
@@ -1,19 +0,0 @@
|
||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
-
|
||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
-
|
||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
variant: v8
|
@@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
s6-svscanctl -t /etc/s6/
|
@@ -1,55 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ ! -d /data/git/.ssh ]; then
|
||||
mkdir -p /data/git/.ssh
|
||||
chmod 700 /data/git/.ssh
|
||||
fi
|
||||
|
||||
if [ ! -f /data/git/.ssh/environment ]; then
|
||||
echo "GITEA_CUSTOM=$GITEA_CUSTOM" >| /data/git/.ssh/environment
|
||||
chmod 600 /data/git/.ssh/environment
|
||||
|
||||
elif ! grep -q "^GITEA_CUSTOM=$GITEA_CUSTOM$" /data/git/.ssh/environment; then
|
||||
sed -i /^GITEA_CUSTOM=/d /data/git/.ssh/environment
|
||||
echo "GITEA_CUSTOM=$GITEA_CUSTOM" >> /data/git/.ssh/environment
|
||||
fi
|
||||
|
||||
if [ ! -f ${GITEA_CUSTOM}/conf/app.ini ]; then
|
||||
mkdir -p ${GITEA_CUSTOM}/conf
|
||||
|
||||
# Set INSTALL_LOCK to true only if SECRET_KEY is not empty and
|
||||
# INSTALL_LOCK is empty
|
||||
if [ -n "$SECRET_KEY" ] && [ -z "$INSTALL_LOCK" ]; then
|
||||
INSTALL_LOCK=true
|
||||
fi
|
||||
|
||||
# Substitude the environment variables in the template
|
||||
APP_NAME=${APP_NAME:-"Gitea: Git with a cup of tea"} \
|
||||
RUN_MODE=${RUN_MODE:-"prod"} \
|
||||
DOMAIN=${DOMAIN:-"localhost"} \
|
||||
SSH_DOMAIN=${SSH_DOMAIN:-"localhost"} \
|
||||
HTTP_PORT=${HTTP_PORT:-"3000"} \
|
||||
ROOT_URL=${ROOT_URL:-""} \
|
||||
DISABLE_SSH=${DISABLE_SSH:-"false"} \
|
||||
SSH_PORT=${SSH_PORT:-"22"} \
|
||||
SSH_LISTEN_PORT=${SSH_LISTEN_PORT:-"${SSH_PORT}"} \
|
||||
LFS_START_SERVER=${LFS_START_SERVER:-"false"} \
|
||||
DB_TYPE=${DB_TYPE:-"sqlite3"} \
|
||||
DB_HOST=${DB_HOST:-"localhost:3306"} \
|
||||
DB_NAME=${DB_NAME:-"gitea"} \
|
||||
DB_USER=${DB_USER:-"root"} \
|
||||
DB_PASSWD=${DB_PASSWD:-""} \
|
||||
INSTALL_LOCK=${INSTALL_LOCK:-"false"} \
|
||||
DISABLE_REGISTRATION=${DISABLE_REGISTRATION:-"false"} \
|
||||
REQUIRE_SIGNIN_VIEW=${REQUIRE_SIGNIN_VIEW:-"false"} \
|
||||
SECRET_KEY=${SECRET_KEY:-""} \
|
||||
envsubst < /etc/templates/app.ini > ${GITEA_CUSTOM}/conf/app.ini
|
||||
|
||||
chown ${USER}:git ${GITEA_CUSTOM}/conf/app.ini
|
||||
fi
|
||||
|
||||
# only chown if current owner is not already the gitea ${USER}. No recursive check to save time
|
||||
if ! [[ $(ls -ld /data/gitea | awk '{print $3}') = ${USER} ]]; then chown -R ${USER}:git /data/gitea; fi
|
||||
if ! [[ $(ls -ld /app/gitea | awk '{print $3}') = ${USER} ]]; then chown -R ${USER}:git /app/gitea; fi
|
||||
if ! [[ $(ls -ld /data/git | awk '{print $3}') = ${USER} ]]; then chown -R ${USER}:git /data/git; fi
|
||||
chmod 0755 /data/gitea /app/gitea /data/git
|
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
[[ -f ./setup ]] && source ./setup
|
||||
|
||||
pushd /root > /dev/null
|
||||
exec su-exec root /usr/sbin/sshd -D -e 2>&1
|
||||
popd
|
@@ -1,37 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ ! -d /data/ssh ]; then
|
||||
mkdir -p /data/ssh
|
||||
fi
|
||||
|
||||
if [ ! -f /data/ssh/ssh_host_ed25519_key ]; then
|
||||
echo "Generating /data/ssh/ssh_host_ed25519_key..."
|
||||
ssh-keygen -t ed25519 -f /data/ssh/ssh_host_ed25519_key -N "" > /dev/null
|
||||
fi
|
||||
|
||||
if [ ! -f /data/ssh/ssh_host_rsa_key ]; then
|
||||
echo "Generating /data/ssh/ssh_host_rsa_key..."
|
||||
ssh-keygen -t rsa -b 2048 -f /data/ssh/ssh_host_rsa_key -N "" > /dev/null
|
||||
fi
|
||||
|
||||
if [ ! -f /data/ssh/ssh_host_dsa_key ]; then
|
||||
echo "Generating /data/ssh/ssh_host_dsa_key..."
|
||||
ssh-keygen -t dsa -f /data/ssh/ssh_host_dsa_key -N "" > /dev/null
|
||||
fi
|
||||
|
||||
if [ ! -f /data/ssh/ssh_host_ecdsa_key ]; then
|
||||
echo "Generating /data/ssh/ssh_host_ecdsa_key..."
|
||||
ssh-keygen -t ecdsa -b 256 -f /data/ssh/ssh_host_ecdsa_key -N "" > /dev/null
|
||||
fi
|
||||
|
||||
if [ -d /etc/ssh ]; then
|
||||
SSH_PORT=${SSH_PORT:-"22"} \
|
||||
SSH_LISTEN_PORT=${SSH_LISTEN_PORT:-"${SSH_PORT}"} \
|
||||
envsubst < /etc/templates/sshd_config > /etc/ssh/sshd_config
|
||||
|
||||
chmod 0644 /etc/ssh/sshd_config
|
||||
fi
|
||||
|
||||
chown root:root /data/ssh/*
|
||||
chmod 0700 /data/ssh
|
||||
chmod 0600 /data/ssh/*
|
@@ -1,61 +0,0 @@
|
||||
APP_NAME = $APP_NAME
|
||||
RUN_MODE = $RUN_MODE
|
||||
|
||||
[repository]
|
||||
ROOT = /data/git/repositories
|
||||
|
||||
[repository.local]
|
||||
LOCAL_COPY_PATH = /data/gitea/tmp/local-repo
|
||||
|
||||
[repository.upload]
|
||||
TEMP_PATH = /data/gitea/uploads
|
||||
|
||||
[server]
|
||||
APP_DATA_PATH = /data/gitea
|
||||
DOMAIN = $DOMAIN
|
||||
SSH_DOMAIN = $SSH_DOMAIN
|
||||
HTTP_PORT = $HTTP_PORT
|
||||
ROOT_URL = $ROOT_URL
|
||||
DISABLE_SSH = $DISABLE_SSH
|
||||
SSH_PORT = $SSH_PORT
|
||||
SSH_LISTEN_PORT = $SSH_LISTEN_PORT
|
||||
LFS_START_SERVER = $LFS_START_SERVER
|
||||
LFS_CONTENT_PATH = /data/git/lfs
|
||||
|
||||
[database]
|
||||
PATH = /data/gitea/gitea.db
|
||||
DB_TYPE = $DB_TYPE
|
||||
HOST = $DB_HOST
|
||||
NAME = $DB_NAME
|
||||
USER = $DB_USER
|
||||
PASSWD = $DB_PASSWD
|
||||
LOG_SQL = false
|
||||
|
||||
[indexer]
|
||||
ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve
|
||||
|
||||
[session]
|
||||
PROVIDER_CONFIG = /data/gitea/sessions
|
||||
|
||||
[picture]
|
||||
AVATAR_UPLOAD_PATH = /data/gitea/avatars
|
||||
REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars
|
||||
|
||||
[attachment]
|
||||
PATH = /data/gitea/attachments
|
||||
|
||||
[log]
|
||||
MODE = console
|
||||
LEVEL = info
|
||||
REDIRECT_MACARON_LOG = true
|
||||
MACARON = console
|
||||
ROUTER = console
|
||||
ROOT_PATH = /data/gitea/log
|
||||
|
||||
[security]
|
||||
INSTALL_LOCK = $INSTALL_LOCK
|
||||
SECRET_KEY = $SECRET_KEY
|
||||
|
||||
[service]
|
||||
DISABLE_REGISTRATION = $DISABLE_REGISTRATION
|
||||
REQUIRE_SIGNIN_VIEW = $REQUIRE_SIGNIN_VIEW
|
@@ -1,40 +0,0 @@
|
||||
Port ${SSH_LISTEN_PORT}
|
||||
Protocol 2
|
||||
|
||||
AddressFamily any
|
||||
ListenAddress 0.0.0.0
|
||||
ListenAddress ::
|
||||
|
||||
LogLevel INFO
|
||||
|
||||
HostKey /data/ssh/ssh_host_ed25519_key
|
||||
HostCertificate /data/ssh/ssh_host_ed25519_cert
|
||||
HostKey /data/ssh/ssh_host_rsa_key
|
||||
HostCertificate /data/ssh/ssh_host_rsa_cert
|
||||
HostKey /data/ssh/ssh_host_ecdsa_key
|
||||
HostCertificate /data/ssh/ssh_host_ecdsa_cert
|
||||
HostKey /data/ssh/ssh_host_dsa_key
|
||||
HostCertificate /data/ssh/ssh_host_dsa_cert
|
||||
|
||||
AuthorizedKeysFile .ssh/authorized_keys
|
||||
AuthorizedPrincipalsFile .ssh/authorized_principals
|
||||
TrustedUserCAKeys /data/git/.ssh/gitea-trusted-user-ca-keys.pem
|
||||
CASignatureAlgorithms ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,sk-ecdsa-sha2-nistp256@openssh.com,ssh-ed25519,sk-ssh-ed25519@openssh.com,rsa-sha2-512,rsa-sha2-256,ssh-rsa
|
||||
|
||||
UseDNS no
|
||||
AllowAgentForwarding no
|
||||
AllowTcpForwarding no
|
||||
PrintMotd no
|
||||
|
||||
PermitUserEnvironment yes
|
||||
PermitRootLogin no
|
||||
ChallengeResponseAuthentication no
|
||||
PasswordAuthentication no
|
||||
PermitEmptyPasswords no
|
||||
|
||||
AllowUsers ${USER}
|
||||
|
||||
Banner none
|
||||
Subsystem sftp /usr/lib/ssh/sftp-server
|
||||
|
||||
AcceptEnv GIT_PROTOCOL
|
@@ -3,6 +3,8 @@
|
||||
if [ "${USER}" != "git" ]; then
|
||||
# rename user
|
||||
sed -i -e "s/^git\:/${USER}\:/g" /etc/passwd
|
||||
# switch sshd config to different user
|
||||
sed -i -e "s/AllowUsers git$/AllowUsers ${USER}/g" /etc/ssh/sshd_config
|
||||
fi
|
||||
|
||||
if [ -z "${USER_GID}" ]; then
|
1
docs/.gitignore
vendored
1
docs/.gitignore
vendored
@@ -1,4 +1,3 @@
|
||||
public/
|
||||
templates/swagger/v1_json.tmpl
|
||||
themes/
|
||||
resources/
|
||||
|
@@ -21,10 +21,6 @@ server: $(THEME)
|
||||
build: $(THEME)
|
||||
hugo --cleanDestinationDir
|
||||
|
||||
.PHONY: build-offline
|
||||
build-offline: $(THEME)
|
||||
hugo --baseURL="/" --cleanDestinationDir
|
||||
|
||||
.PHONY: update
|
||||
update: $(THEME)
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# Gitea: 文档
|
||||
|
||||
[](http://drone.gitea.io/go-gitea/docs)
|
||||
[](https://discord.gg/Gitea)
|
||||
[](https://discord.gg/NsatcWJ)
|
||||
[](http://microbadger.com/images/gitea/docs "Get your own image badge on microbadger.com")
|
||||
|
||||
## 关于托管方式
|
||||
|
@@ -1,174 +0,0 @@
|
||||
/* global Fuse, Mark */
|
||||
|
||||
function ready(fn) {
|
||||
if (document.readyState !== 'loading') {
|
||||
fn();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', fn);
|
||||
}
|
||||
}
|
||||
|
||||
ready(doSearch);
|
||||
|
||||
const summaryInclude = 60;
|
||||
const fuseOptions = {
|
||||
shouldSort: true,
|
||||
includeMatches: true,
|
||||
matchAllTokens: true,
|
||||
threshold: 0.0, // for parsing diacritics
|
||||
tokenize: true,
|
||||
location: 0,
|
||||
distance: 100,
|
||||
maxPatternLength: 32,
|
||||
minMatchCharLength: 1,
|
||||
keys: [{
|
||||
name: 'title',
|
||||
weight: 0.8
|
||||
},
|
||||
{
|
||||
name: 'contents',
|
||||
weight: 0.5
|
||||
},
|
||||
{
|
||||
name: 'tags',
|
||||
weight: 0.3
|
||||
},
|
||||
{
|
||||
name: 'categories',
|
||||
weight: 0.3
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
function param(name) {
|
||||
return decodeURIComponent((window.location.search.split(`${name}=`)[1] || '').split('&')[0]).replace(/\+/g, ' ');
|
||||
}
|
||||
|
||||
const searchQuery = param('s');
|
||||
|
||||
function doSearch() {
|
||||
if (searchQuery) {
|
||||
document.getElementById('search-query').value = searchQuery;
|
||||
executeSearch(searchQuery);
|
||||
} else {
|
||||
const para = document.createElement('P');
|
||||
para.innerText = 'Please enter a word or phrase above';
|
||||
document.getElementById('search-results').appendChild(para);
|
||||
}
|
||||
}
|
||||
|
||||
function getJSON(url, fn) {
|
||||
const request = new XMLHttpRequest();
|
||||
request.open('GET', url, true);
|
||||
request.onload = function () {
|
||||
if (request.status >= 200 && request.status < 400) {
|
||||
const data = JSON.parse(request.responseText);
|
||||
fn(data);
|
||||
} else {
|
||||
console.error(`Target reached on ${url} with error ${request.status}`);
|
||||
}
|
||||
};
|
||||
request.onerror = function () {
|
||||
console.error(`Connection error ${request.status}`);
|
||||
};
|
||||
request.send();
|
||||
}
|
||||
|
||||
function executeSearch(searchQuery) {
|
||||
getJSON(`/${document.LANG}/index.json`, (data) => {
|
||||
const pages = data;
|
||||
const fuse = new Fuse(pages, fuseOptions);
|
||||
const result = fuse.search(searchQuery);
|
||||
document.getElementById('search-results').innerHTML = '';
|
||||
if (result.length > 0) {
|
||||
populateResults(result);
|
||||
} else {
|
||||
const para = document.createElement('P');
|
||||
para.innerText = 'No matches found';
|
||||
document.getElementById('search-results').appendChild(para);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function populateResults(result) {
|
||||
result.forEach((value, key) => {
|
||||
const content = value.item.contents;
|
||||
let snippet = '';
|
||||
const snippetHighlights = [];
|
||||
if (fuseOptions.tokenize) {
|
||||
snippetHighlights.push(searchQuery);
|
||||
value.matches.forEach((mvalue) => {
|
||||
if (mvalue.key === 'tags' || mvalue.key === 'categories') {
|
||||
snippetHighlights.push(mvalue.value);
|
||||
} else if (mvalue.key === 'contents') {
|
||||
const ind = content.toLowerCase().indexOf(searchQuery.toLowerCase());
|
||||
const start = ind - summaryInclude > 0 ? ind - summaryInclude : 0;
|
||||
const end = ind + searchQuery.length + summaryInclude < content.length ? ind + searchQuery.length + summaryInclude : content.length;
|
||||
snippet += content.substring(start, end);
|
||||
if (ind > -1) {
|
||||
snippetHighlights.push(content.substring(ind, ind + searchQuery.length));
|
||||
} else {
|
||||
snippetHighlights.push(mvalue.value.substring(mvalue.indices[0][0], mvalue.indices[0][1] - mvalue.indices[0][0] + 1));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (snippet.length < 1) {
|
||||
snippet += content.substring(0, summaryInclude * 2);
|
||||
}
|
||||
// pull template from hugo template definition
|
||||
const templateDefinition = document.getElementById('search-result-template').innerHTML;
|
||||
// replace values
|
||||
const output = render(templateDefinition, {
|
||||
key,
|
||||
title: value.item.title,
|
||||
link: value.item.permalink,
|
||||
tags: value.item.tags,
|
||||
categories: value.item.categories,
|
||||
snippet
|
||||
});
|
||||
document.getElementById('search-results').appendChild(htmlToElement(output));
|
||||
|
||||
snippetHighlights.forEach((snipvalue) => {
|
||||
new Mark(document.getElementById(`summary-${key}`)).mark(snipvalue);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function render(templateString, data) {
|
||||
let conditionalMatches, copy;
|
||||
const conditionalPattern = /\$\{\s*isset ([a-zA-Z]*) \s*\}(.*)\$\{\s*end\s*}/g;
|
||||
// since loop below depends on re.lastInxdex, we use a copy to capture any manipulations whilst inside the loop
|
||||
copy = templateString;
|
||||
while ((conditionalMatches = conditionalPattern.exec(templateString)) !== null) {
|
||||
if (data[conditionalMatches[1]]) {
|
||||
// valid key, remove conditionals, leave content.
|
||||
copy = copy.replace(conditionalMatches[0], conditionalMatches[2]);
|
||||
} else {
|
||||
// not valid, remove entire section
|
||||
copy = copy.replace(conditionalMatches[0], '');
|
||||
}
|
||||
}
|
||||
templateString = copy;
|
||||
// now any conditionals removed we can do simple substitution
|
||||
let key, find, re;
|
||||
for (key of Object.keys(data)) {
|
||||
find = `\\$\\{\\s*${key}\\s*\\}`;
|
||||
re = new RegExp(find, 'g');
|
||||
templateString = templateString.replace(re, data[key]);
|
||||
}
|
||||
return templateString;
|
||||
}
|
||||
|
||||
/**
|
||||
* By Mark Amery: https://stackoverflow.com/a/35385518
|
||||
* @param {String} HTML representing a single element
|
||||
* @return {Element}
|
||||
*/
|
||||
function htmlToElement(html) {
|
||||
const template = document.createElement('template');
|
||||
html = html.trim(); // Never return a text node of whitespace as the result
|
||||
template.innerHTML = html;
|
||||
return template.content.firstChild;
|
||||
}
|
@@ -6,7 +6,6 @@ theme: gitea
|
||||
defaultContentLanguage: en-us
|
||||
defaultContentLanguageInSubdir: true
|
||||
enableMissingTranslationPlaceholders: true
|
||||
enableEmoji: true
|
||||
|
||||
permalinks:
|
||||
post: /:year/:month/:title/
|
||||
@@ -18,16 +17,6 @@ params:
|
||||
description: Git with a cup of tea
|
||||
author: The Gitea Authors
|
||||
website: https://docs.gitea.io
|
||||
version: 1.12.5
|
||||
minGoVersion: 1.13
|
||||
goVersion: 1.15
|
||||
minNodeVersion: 10.13
|
||||
|
||||
outputs:
|
||||
home:
|
||||
- HTML
|
||||
- RSS
|
||||
- JSON
|
||||
|
||||
menu:
|
||||
page:
|
||||
@@ -52,10 +41,6 @@ menu:
|
||||
url: https://code.gitea.io/
|
||||
weight: 40
|
||||
pre: code
|
||||
- name: Translation
|
||||
url: https://crowdin.com/project/gitea
|
||||
weight: 41
|
||||
pre: language
|
||||
- name: Downloads
|
||||
url: https://dl.gitea.io/
|
||||
weight: 50
|
||||
@@ -65,7 +50,7 @@ menu:
|
||||
weight: 60
|
||||
pre: github
|
||||
- name: Discord Chat
|
||||
url: https://discord.gg/Gitea
|
||||
url: https://discord.gg/NsatcWJ
|
||||
weight: 70
|
||||
pre: comment
|
||||
- name: Forum
|
||||
@@ -104,10 +89,6 @@ languages:
|
||||
url: https://code.gitea.io/
|
||||
weight: 40
|
||||
pre: code
|
||||
- name: 翻译
|
||||
url: https://crowdin.com/project/gitea
|
||||
weight: 41
|
||||
pre: language
|
||||
- name: 下载
|
||||
url: https://dl.gitea.io/
|
||||
weight: 50
|
||||
@@ -117,7 +98,7 @@ languages:
|
||||
weight: 60
|
||||
pre: github
|
||||
- name: Discord Chat
|
||||
url: https://discord.gg/Gitea
|
||||
url: https://discord.gg/NsatcWJ
|
||||
weight: 70
|
||||
pre: comment
|
||||
- name: Forum
|
||||
@@ -151,10 +132,6 @@ languages:
|
||||
url: https://code.gitea.io/
|
||||
weight: 40
|
||||
pre: code
|
||||
- name: 翻译
|
||||
url: https://crowdin.com/project/gitea
|
||||
weight: 41
|
||||
pre: language
|
||||
- name: 下载
|
||||
url: https://dl.gitea.io/
|
||||
weight: 50
|
||||
@@ -164,7 +141,7 @@ languages:
|
||||
weight: 60
|
||||
pre: github
|
||||
- name: Discord Chat
|
||||
url: https://discord.gg/Gitea
|
||||
url: https://discord.gg/NsatcWJ
|
||||
weight: 70
|
||||
pre: comment
|
||||
- name: Forum
|
||||
@@ -198,10 +175,6 @@ languages:
|
||||
url: https://code.gitea.io/
|
||||
weight: 40
|
||||
pre: code
|
||||
- name: Translation
|
||||
url: https://crowdin.com/project/gitea
|
||||
weight: 41
|
||||
pre: language
|
||||
- name: Downloads
|
||||
url: https://dl.gitea.io/
|
||||
weight: 50
|
||||
@@ -211,7 +184,7 @@ languages:
|
||||
weight: 60
|
||||
pre: github
|
||||
- name: Chat no Discord
|
||||
url: https://discord.gg/Gitea
|
||||
url: https://discord.gg/NsatcWJ
|
||||
weight: 70
|
||||
pre: comment
|
||||
- name: Forum
|
||||
@@ -245,10 +218,6 @@ languages:
|
||||
url: https://code.gitea.io/
|
||||
weight: 40
|
||||
pre: code
|
||||
- name: Translation
|
||||
url: https://crowdin.com/project/gitea
|
||||
weight: 41
|
||||
pre: language
|
||||
- name: Downloads
|
||||
url: https://dl.gitea.io/
|
||||
weight: 50
|
||||
@@ -258,7 +227,7 @@ languages:
|
||||
weight: 60
|
||||
pre: github
|
||||
- name: Discord Chat
|
||||
url: https://discord.gg/Gitea
|
||||
url: https://discord.gg/NsatcWJ
|
||||
weight: 70
|
||||
pre: comment
|
||||
- name: Forum
|
||||
@@ -292,10 +261,6 @@ languages:
|
||||
url: https://code.gitea.io/
|
||||
weight: 40
|
||||
pre: code
|
||||
- name: Translation
|
||||
url: https://crowdin.com/project/gitea
|
||||
weight: 41
|
||||
pre: language
|
||||
- name: Téléchargement
|
||||
url: https://dl.gitea.io/
|
||||
weight: 50
|
||||
@@ -305,57 +270,10 @@ languages:
|
||||
weight: 60
|
||||
pre: github
|
||||
- name: Discord Chat
|
||||
url: https://discord.gg/Gitea
|
||||
url: https://discord.gg/NsatcWJ
|
||||
weight: 70
|
||||
pre: comment
|
||||
- name: Forum
|
||||
url: https://discourse.gitea.io/
|
||||
weight: 80
|
||||
pre: group
|
||||
|
||||
pt-pt:
|
||||
weight: 6
|
||||
languageName: Português de Portugal
|
||||
menu:
|
||||
page:
|
||||
- name: Página inicial
|
||||
url: https://gitea.io/pt-pt/
|
||||
weight: 10
|
||||
pre: home
|
||||
- name: Documentação
|
||||
url: /pt-pt/
|
||||
weight: 20
|
||||
pre: question
|
||||
post: active
|
||||
- name: API
|
||||
url: https://try.gitea.io/api/swagger
|
||||
weight: 45
|
||||
pre: plug
|
||||
- name: Blog
|
||||
url: https://blog.gitea.io/
|
||||
weight: 30
|
||||
pre: rss
|
||||
- name: Código-fonte
|
||||
url: https://code.gitea.io/
|
||||
weight: 40
|
||||
pre: code
|
||||
- name: Tradução
|
||||
url: https://crowdin.com/project/gitea
|
||||
weight: 41
|
||||
pre: language
|
||||
- name: Descarregamentos
|
||||
url: https://dl.gitea.io/
|
||||
weight: 50
|
||||
pre: download
|
||||
- name: GitHub
|
||||
url: https://github.com/go-gitea/
|
||||
weight: 60
|
||||
pre: github
|
||||
- name: Discussão no Discord
|
||||
url: https://discord.gg/Gitea
|
||||
weight: 70
|
||||
pre: comment
|
||||
- name: Fórum
|
||||
url: https://discourse.gitea.io/
|
||||
weight: 80
|
||||
pre: group
|
||||
|
@@ -1,38 +0,0 @@
|
||||
---
|
||||
date: "2019-12-28"
|
||||
title: "Adding Legal Pages"
|
||||
slug: adding-legal-pages
|
||||
weight: 9
|
||||
toc: false
|
||||
draft: false
|
||||
menu:
|
||||
sidebar:
|
||||
parent: "advanced"
|
||||
name: "Adding Legal Pages"
|
||||
identifier: "adding-legal-pages"
|
||||
weight: 9
|
||||
---
|
||||
|
||||
Some jurisdictions (such as EU), requires certain legal pages (e.g. Privacy Policy) to be added to website. Follow these steps to add them to your Gitea instance.
|
||||
|
||||
## Getting Pages
|
||||
|
||||
Gitea source code ships with sample pages, available in `contrib/legal` directory. Copy them to `custom/public/`. For example, to add Privacy Policy:
|
||||
|
||||
```
|
||||
wget -O /path/to/custom/public/privacy.html https://raw.githubusercontent.com/go-gitea/gitea/master/contrib/legal/privacy.html.sample
|
||||
```
|
||||
|
||||
Now you need to edit the page to meet your requirements. In particular you must change the email addresses, web addresses and references to "Your Gitea Instance" to match your situation.
|
||||
|
||||
You absolutely must not place a general ToS or privacy statement that implies that the gitea project is responsible for your server.
|
||||
|
||||
## Make it Visible
|
||||
|
||||
Create or append to `/path/to/custom/templates/custom/extra_links_footer.tmpl`:
|
||||
|
||||
```go
|
||||
<a class="item" href="{{AppSubUrl}}/privacy.html">Privacy Policy</a>
|
||||
```
|
||||
|
||||
Restart Gitea to see the changes.
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user