Compare commits
37 Commits
main
...
release/v1
Author | SHA1 | Date |
---|---|---|
Lauris BH | 86d61bbb10 | |
Lauris BH | bfe13a28c2 | |
Jonas Franz | ed27da4b0a | |
Ethan Koenig | 88c363f933 | |
Sandro Santilli | f33cd3c676 | |
Sandro Santilli | fecf9390ef | |
Lunny Xiao | bdf1856011 | |
Lunny Xiao | 6037f183cf | |
Lauris BH | 4b187f5167 | |
Lunny Xiao | aee45d072e | |
Lauris BH | e893aced89 | |
Lauris BH | d63ca66623 | |
Lauris BH | 59afb62ab2 | |
Ethan Koenig | 8a19c6b9a2 | |
Lunny Xiao | 81fd8c8fb6 | |
Lauris BH | fd7686171e | |
Ethan Koenig | ec6718ef40 | |
Ethan Koenig | 8f7054a864 | |
Ethan Koenig | 84352316a9 | |
Ethan Koenig | 237df2f339 | |
Lunny Xiao | b9abcb3b61 | |
Lunny Xiao | 9832b9509d | |
Lunny Xiao | 3d688bd2cc | |
Stephan Sachse | ce4a52c22c | |
Lauris BH | 974cffead9 | |
Lauris BH | 8cdd5a33cd | |
Lauris BH | 992ee21a29 | |
Lauris BH | 5ec9c45661 | |
Duncan Ogilvie | b5bdb0474a | |
techknowlogick | 751f01936b | |
Ethan Koenig | b16e5a42eb | |
Lauris BH | 460d0d9077 | |
Lunny Xiao | 9c07d909e6 | |
Ethan Koenig | 93d1ec4514 | |
Lauris BH | be41955407 | |
Lauris BH | c31e8777b7 | |
Lunny Xiao | 4683e540ad |
|
@ -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
|
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:
|
groups:
|
||||||
-
|
-
|
||||||
name: BREAKING
|
name: BREAKING
|
||||||
labels:
|
labels:
|
||||||
- kind/breaking
|
- kind/breaking
|
||||||
-
|
-
|
||||||
name: SECURITY
|
name: FEATURE
|
||||||
labels:
|
|
||||||
- kind/security
|
|
||||||
-
|
|
||||||
name: FEATURES
|
|
||||||
labels:
|
labels:
|
||||||
- kind/feature
|
- kind/feature
|
||||||
-
|
-
|
||||||
name: API
|
name: BUGFIXES
|
||||||
labels:
|
labels:
|
||||||
- kind/api
|
- kind/bug
|
||||||
-
|
-
|
||||||
name: ENHANCEMENTS
|
name: ENHANCEMENT
|
||||||
labels:
|
labels:
|
||||||
- kind/enhancement
|
- kind/enhancement
|
||||||
- kind/refactor
|
- kind/refactor
|
||||||
- kind/ui
|
- kind/ui
|
||||||
-
|
-
|
||||||
name: BUGFIXES
|
name: SECURITY
|
||||||
labels:
|
labels:
|
||||||
- kind/bug
|
- kind/security
|
||||||
-
|
-
|
||||||
name: TESTING
|
name: TESTING
|
||||||
labels:
|
labels:
|
||||||
- kind/testing
|
- kind/testing
|
||||||
-
|
-
|
||||||
name: TRANSLATION
|
name: TRANSLATION
|
||||||
labels:
|
labels:
|
||||||
- kind/translation
|
- kind/translation
|
||||||
-
|
-
|
||||||
name: BUILD
|
name: BUILD
|
||||||
labels:
|
labels:
|
||||||
- kind/build
|
- kind/build
|
||||||
- kind/lint
|
- kind/lint
|
||||||
-
|
-
|
||||||
name: DOCS
|
name: DOCS
|
||||||
labels:
|
labels:
|
||||||
- kind/docs
|
- kind/docs
|
||||||
-
|
-
|
||||||
name: MISC
|
name: MISC
|
||||||
default: true
|
default: true
|
||||||
|
|
||||||
# regex indicating which labels to skip for the changelog
|
|
||||||
skip-labels: skip-changelog|backport\/.+
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
*
|
||||||
|
!gitea
|
||||||
|
!docker
|
||||||
|
!public
|
||||||
|
!templates
|
1420
.drone.yml
1420
.drone.yml
File diff suppressed because it is too large
Load Diff
|
@ -1,31 +1,31 @@
|
||||||
|
# http://editorconfig.org
|
||||||
|
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
[*]
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.go]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 8
|
||||||
|
|
||||||
|
[*.{tmpl,html}]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.{less}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.{yml}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
tab_width = 2
|
|
||||||
end_of_line = lf
|
|
||||||
charset = utf-8
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
|
||||||
|
|
||||||
[*.{go,tmpl,html}]
|
[*.js]
|
||||||
indent_style = tab
|
|
||||||
|
|
||||||
[templates/custom/*.tmpl]
|
|
||||||
insert_final_newline = false
|
|
||||||
|
|
||||||
[templates/swagger/v1_json.tmpl]
|
|
||||||
indent_style = space
|
|
||||||
|
|
||||||
[templates/user/auth/oidc_wellknown.tmpl]
|
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
[Makefile]
|
[Makefile]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
|
||||||
[*.svg]
|
|
||||||
insert_final_newline = false
|
|
||||||
|
|
||||||
[*.md]
|
|
||||||
trim_trailing_whitespace = false
|
|
||||||
|
|
452
.eslintrc
452
.eslintrc
|
@ -1,452 +0,0 @@
|
||||||
root: true
|
|
||||||
reportUnusedDisableDirectives: true
|
|
||||||
|
|
||||||
ignorePatterns:
|
|
||||||
- /web_src/js/vendor
|
|
||||||
- /templates/base/head.tmpl
|
|
||||||
- /templates/repo/activity.tmpl
|
|
||||||
- /templates/repo/view_file.tmpl
|
|
||||||
|
|
||||||
parserOptions:
|
|
||||||
sourceType: module
|
|
||||||
ecmaVersion: 2021
|
|
||||||
|
|
||||||
plugins:
|
|
||||||
- eslint-plugin-unicorn
|
|
||||||
- eslint-plugin-import
|
|
||||||
- eslint-plugin-vue
|
|
||||||
- eslint-plugin-html
|
|
||||||
|
|
||||||
extends:
|
|
||||||
- plugin:vue/recommended
|
|
||||||
|
|
||||||
env:
|
|
||||||
es2021: true
|
|
||||||
node: true
|
|
||||||
|
|
||||||
globals:
|
|
||||||
__webpack_public_path__: true
|
|
||||||
CodeMirror: false
|
|
||||||
Dropzone: false
|
|
||||||
SimpleMDE: false
|
|
||||||
u2fApi: false
|
|
||||||
|
|
||||||
settings:
|
|
||||||
html/html-extensions: [".tmpl"]
|
|
||||||
|
|
||||||
overrides:
|
|
||||||
- files: ["web_src/**/*.js", "web_src/**/*.vue", "templates/**/*.tmpl"]
|
|
||||||
env:
|
|
||||||
browser: true
|
|
||||||
jquery: true
|
|
||||||
node: false
|
|
||||||
- files: ["templates/**/*.tmpl"]
|
|
||||||
rules:
|
|
||||||
no-tabs: [0]
|
|
||||||
indent: [2, tab, {SwitchCase: 1}]
|
|
||||||
- files: ["web_src/**/*worker.js"]
|
|
||||||
env:
|
|
||||||
worker: true
|
|
||||||
rules:
|
|
||||||
no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, location, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, status, statusbar, stop, toolbar, top]
|
|
||||||
- files: ["build/generate-images.js"]
|
|
||||||
rules:
|
|
||||||
import/no-unresolved: [0]
|
|
||||||
import/no-extraneous-dependencies: [0]
|
|
||||||
- files: ["*.test.js"]
|
|
||||||
env:
|
|
||||||
jest: true
|
|
||||||
- files: ["*.config.js"]
|
|
||||||
rules:
|
|
||||||
import/no-unused-modules: [0]
|
|
||||||
|
|
||||||
rules:
|
|
||||||
accessor-pairs: [2]
|
|
||||||
array-bracket-newline: [0]
|
|
||||||
array-bracket-spacing: [2, never]
|
|
||||||
array-callback-return: [0]
|
|
||||||
array-element-newline: [0]
|
|
||||||
arrow-body-style: [0]
|
|
||||||
arrow-parens: [2, always]
|
|
||||||
arrow-spacing: [2, {before: true, after: true}]
|
|
||||||
block-scoped-var: [2]
|
|
||||||
brace-style: [2, 1tbs, {allowSingleLine: true}]
|
|
||||||
camelcase: [0]
|
|
||||||
capitalized-comments: [0]
|
|
||||||
class-methods-use-this: [0]
|
|
||||||
comma-dangle: [2, only-multiline]
|
|
||||||
comma-spacing: [2, {before: false, after: true}]
|
|
||||||
comma-style: [2, last]
|
|
||||||
complexity: [0]
|
|
||||||
computed-property-spacing: [2, never]
|
|
||||||
consistent-return: [0]
|
|
||||||
consistent-this: [0]
|
|
||||||
constructor-super: [2]
|
|
||||||
curly: [0]
|
|
||||||
default-case-last: [2]
|
|
||||||
default-case: [0]
|
|
||||||
default-param-last: [0]
|
|
||||||
dot-location: [2, property]
|
|
||||||
dot-notation: [0]
|
|
||||||
eol-last: [2]
|
|
||||||
eqeqeq: [2]
|
|
||||||
for-direction: [2]
|
|
||||||
func-call-spacing: [2, never]
|
|
||||||
func-name-matching: [2]
|
|
||||||
func-names: [0]
|
|
||||||
func-style: [0]
|
|
||||||
function-call-argument-newline: [0]
|
|
||||||
function-paren-newline: [0]
|
|
||||||
generator-star-spacing: [0]
|
|
||||||
getter-return: [2]
|
|
||||||
grouped-accessor-pairs: [2]
|
|
||||||
guard-for-in: [0]
|
|
||||||
id-blacklist: [0]
|
|
||||||
id-length: [0]
|
|
||||||
id-match: [0]
|
|
||||||
implicit-arrow-linebreak: [0]
|
|
||||||
import/default: [0]
|
|
||||||
import/dynamic-import-chunkname: [0]
|
|
||||||
import/export: [2]
|
|
||||||
import/exports-last: [0]
|
|
||||||
import/extensions: [2, always, {ignorePackages: true}]
|
|
||||||
import/first: [2]
|
|
||||||
import/group-exports: [0]
|
|
||||||
import/max-dependencies: [0]
|
|
||||||
import/named: [2]
|
|
||||||
import/namespace: [0]
|
|
||||||
import/newline-after-import: [0]
|
|
||||||
import/no-absolute-path: [0]
|
|
||||||
import/no-amd: [0]
|
|
||||||
import/no-anonymous-default-export: [0]
|
|
||||||
import/no-commonjs: [0]
|
|
||||||
import/no-cycle: [2, {ignoreExternal: true}]
|
|
||||||
import/no-default-export: [0]
|
|
||||||
import/no-deprecated: [0]
|
|
||||||
import/no-dynamic-require: [0]
|
|
||||||
import/no-extraneous-dependencies: [2]
|
|
||||||
import/no-import-module-exports: [0]
|
|
||||||
import/no-internal-modules: [0]
|
|
||||||
import/no-mutable-exports: [2]
|
|
||||||
import/no-named-as-default-member: [0]
|
|
||||||
import/no-named-as-default: [2]
|
|
||||||
import/no-named-default: [0]
|
|
||||||
import/no-named-export: [0]
|
|
||||||
import/no-namespace: [0]
|
|
||||||
import/no-nodejs-modules: [0]
|
|
||||||
import/no-relative-packages: [0]
|
|
||||||
import/no-relative-parent-imports: [0]
|
|
||||||
import/no-restricted-paths: [0]
|
|
||||||
import/no-self-import: [2]
|
|
||||||
import/no-unassigned-import: [0]
|
|
||||||
import/no-unresolved: [2, {commonjs: true}]
|
|
||||||
import/no-unused-modules: [2, {unusedExports: true}]
|
|
||||||
import/no-useless-path-segments: [2, {commonjs: true}]
|
|
||||||
import/no-webpack-loader-syntax: [2]
|
|
||||||
import/order: [0]
|
|
||||||
import/prefer-default-export: [0]
|
|
||||||
import/unambiguous: [0]
|
|
||||||
indent: [2, 2, {SwitchCase: 1}]
|
|
||||||
init-declarations: [0]
|
|
||||||
key-spacing: [2]
|
|
||||||
keyword-spacing: [2]
|
|
||||||
line-comment-position: [0]
|
|
||||||
linebreak-style: [2, unix]
|
|
||||||
lines-around-comment: [0]
|
|
||||||
lines-between-class-members: [0]
|
|
||||||
max-classes-per-file: [0]
|
|
||||||
max-depth: [0]
|
|
||||||
max-len: [0]
|
|
||||||
max-lines-per-function: [0]
|
|
||||||
max-lines: [0]
|
|
||||||
max-nested-callbacks: [0]
|
|
||||||
max-params: [0]
|
|
||||||
max-statements-per-line: [0]
|
|
||||||
max-statements: [0]
|
|
||||||
multiline-comment-style: [2, separate-lines]
|
|
||||||
multiline-ternary: [0]
|
|
||||||
new-cap: [0]
|
|
||||||
new-parens: [2]
|
|
||||||
newline-per-chained-call: [0]
|
|
||||||
no-alert: [0]
|
|
||||||
no-array-constructor: [2]
|
|
||||||
no-async-promise-executor: [2]
|
|
||||||
no-await-in-loop: [0]
|
|
||||||
no-bitwise: [0]
|
|
||||||
no-buffer-constructor: [0]
|
|
||||||
no-caller: [2]
|
|
||||||
no-case-declarations: [2]
|
|
||||||
no-class-assign: [2]
|
|
||||||
no-compare-neg-zero: [2]
|
|
||||||
no-cond-assign: [2, except-parens]
|
|
||||||
no-confusing-arrow: [0]
|
|
||||||
no-console: [1, {allow: [info, warn, error]}]
|
|
||||||
no-const-assign: [2]
|
|
||||||
no-constant-condition: [0]
|
|
||||||
no-constructor-return: [2]
|
|
||||||
no-continue: [0]
|
|
||||||
no-control-regex: [0]
|
|
||||||
no-debugger: [1]
|
|
||||||
no-delete-var: [2]
|
|
||||||
no-div-regex: [0]
|
|
||||||
no-dupe-args: [2]
|
|
||||||
no-dupe-class-members: [2]
|
|
||||||
no-dupe-else-if: [2]
|
|
||||||
no-dupe-keys: [2]
|
|
||||||
no-duplicate-case: [2]
|
|
||||||
no-duplicate-imports: [2]
|
|
||||||
no-else-return: [2]
|
|
||||||
no-empty-character-class: [2]
|
|
||||||
no-empty-function: [0]
|
|
||||||
no-empty-pattern: [2]
|
|
||||||
no-empty: [2, {allowEmptyCatch: true}]
|
|
||||||
no-eq-null: [2]
|
|
||||||
no-eval: [2]
|
|
||||||
no-ex-assign: [2]
|
|
||||||
no-extend-native: [2]
|
|
||||||
no-extra-bind: [2]
|
|
||||||
no-extra-boolean-cast: [2]
|
|
||||||
no-extra-label: [0]
|
|
||||||
no-extra-parens: [0]
|
|
||||||
no-extra-semi: [2]
|
|
||||||
no-fallthrough: [2]
|
|
||||||
no-floating-decimal: [0]
|
|
||||||
no-func-assign: [2]
|
|
||||||
no-global-assign: [2]
|
|
||||||
no-implicit-coercion: [0]
|
|
||||||
no-implicit-globals: [0]
|
|
||||||
no-implied-eval: [2]
|
|
||||||
no-import-assign: [2]
|
|
||||||
no-inline-comments: [0]
|
|
||||||
no-inner-declarations: [2]
|
|
||||||
no-invalid-regexp: [2]
|
|
||||||
no-invalid-this: [0]
|
|
||||||
no-irregular-whitespace: [2]
|
|
||||||
no-iterator: [2]
|
|
||||||
no-label-var: [2]
|
|
||||||
no-labels: [2]
|
|
||||||
no-lone-blocks: [2]
|
|
||||||
no-lonely-if: [0]
|
|
||||||
no-loop-func: [0]
|
|
||||||
no-loss-of-precision: [2]
|
|
||||||
no-magic-numbers: [0]
|
|
||||||
no-misleading-character-class: [2]
|
|
||||||
no-mixed-operators: [0]
|
|
||||||
no-mixed-spaces-and-tabs: [2]
|
|
||||||
no-multi-assign: [0]
|
|
||||||
no-multi-spaces: [2, {ignoreEOLComments: true, exceptions: {Property: true}}]
|
|
||||||
no-multi-str: [2]
|
|
||||||
no-negated-condition: [0]
|
|
||||||
no-nested-ternary: [0]
|
|
||||||
no-new-func: [2]
|
|
||||||
no-new-object: [2]
|
|
||||||
no-new-symbol: [2]
|
|
||||||
no-new-wrappers: [2]
|
|
||||||
no-new: [0]
|
|
||||||
no-nonoctal-decimal-escape: [2]
|
|
||||||
no-obj-calls: [2]
|
|
||||||
no-octal-escape: [2]
|
|
||||||
no-octal: [2]
|
|
||||||
no-param-reassign: [0]
|
|
||||||
no-plusplus: [0]
|
|
||||||
no-promise-executor-return: [0]
|
|
||||||
no-proto: [2]
|
|
||||||
no-prototype-builtins: [2]
|
|
||||||
no-redeclare: [2]
|
|
||||||
no-regex-spaces: [2]
|
|
||||||
no-restricted-exports: [0]
|
|
||||||
no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, location, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, self, status, statusbar, stop, toolbar, top]
|
|
||||||
no-restricted-imports: [0]
|
|
||||||
no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement]
|
|
||||||
no-return-assign: [0]
|
|
||||||
no-return-await: [0]
|
|
||||||
no-script-url: [2]
|
|
||||||
no-self-assign: [2, {props: true}]
|
|
||||||
no-self-compare: [2]
|
|
||||||
no-sequences: [2]
|
|
||||||
no-setter-return: [2]
|
|
||||||
no-shadow-restricted-names: [2]
|
|
||||||
no-shadow: [0]
|
|
||||||
no-sparse-arrays: [2]
|
|
||||||
no-tabs: [2]
|
|
||||||
no-template-curly-in-string: [2]
|
|
||||||
no-ternary: [0]
|
|
||||||
no-this-before-super: [2]
|
|
||||||
no-throw-literal: [2]
|
|
||||||
no-trailing-spaces: [2]
|
|
||||||
no-undef-init: [2]
|
|
||||||
no-undef: [2, {typeof: true}]
|
|
||||||
no-undefined: [0]
|
|
||||||
no-underscore-dangle: [0]
|
|
||||||
no-unexpected-multiline: [2]
|
|
||||||
no-unmodified-loop-condition: [2]
|
|
||||||
no-unneeded-ternary: [0]
|
|
||||||
no-unreachable-loop: [2]
|
|
||||||
no-unreachable: [2]
|
|
||||||
no-unsafe-finally: [2]
|
|
||||||
no-unsafe-negation: [2]
|
|
||||||
no-unused-expressions: [2]
|
|
||||||
no-unused-labels: [2]
|
|
||||||
no-unused-vars: [2, {args: all, argsIgnorePattern: ^_, varsIgnorePattern: ^_, caughtErrorsIgnorePattern: ^_, ignoreRestSiblings: false}]
|
|
||||||
no-use-before-define: [2, nofunc]
|
|
||||||
no-useless-backreference: [0]
|
|
||||||
no-useless-call: [2]
|
|
||||||
no-useless-catch: [2]
|
|
||||||
no-useless-computed-key: [2]
|
|
||||||
no-useless-concat: [2]
|
|
||||||
no-useless-constructor: [2]
|
|
||||||
no-useless-escape: [2]
|
|
||||||
no-useless-rename: [2]
|
|
||||||
no-useless-return: [2]
|
|
||||||
no-var: [2]
|
|
||||||
no-void: [2]
|
|
||||||
no-warning-comments: [0]
|
|
||||||
no-whitespace-before-property: [2]
|
|
||||||
no-with: [2]
|
|
||||||
nonblock-statement-body-position: [2]
|
|
||||||
object-curly-newline: [0]
|
|
||||||
object-curly-spacing: [2, never]
|
|
||||||
object-shorthand: [2, always]
|
|
||||||
one-var-declaration-per-line: [0]
|
|
||||||
one-var: [0]
|
|
||||||
operator-assignment: [2, always]
|
|
||||||
operator-linebreak: [2, after]
|
|
||||||
padded-blocks: [2, never]
|
|
||||||
padding-line-between-statements: [0]
|
|
||||||
prefer-arrow-callback: [2, {allowNamedFunctions: true, allowUnboundThis: true}]
|
|
||||||
prefer-const: [2, {destructuring: all}]
|
|
||||||
prefer-destructuring: [0]
|
|
||||||
prefer-exponentiation-operator: [2]
|
|
||||||
prefer-named-capture-group: [0]
|
|
||||||
prefer-numeric-literals: [2]
|
|
||||||
prefer-object-spread: [0]
|
|
||||||
prefer-promise-reject-errors: [2, {allowEmptyReject: false}]
|
|
||||||
prefer-regex-literals: [2]
|
|
||||||
prefer-rest-params: [2]
|
|
||||||
prefer-spread: [2]
|
|
||||||
prefer-template: [2]
|
|
||||||
quote-props: [0]
|
|
||||||
quotes: [2, single, {avoidEscape: true, allowTemplateLiterals: true}]
|
|
||||||
radix: [2, as-needed]
|
|
||||||
require-atomic-updates: [0]
|
|
||||||
require-await: [0]
|
|
||||||
require-unicode-regexp: [0]
|
|
||||||
require-yield: [2]
|
|
||||||
rest-spread-spacing: [2, never]
|
|
||||||
semi-spacing: [2, {before: false, after: true}]
|
|
||||||
semi-style: [2, last]
|
|
||||||
semi: [2, always, {omitLastInOneLineBlock: true}]
|
|
||||||
sort-imports: [0]
|
|
||||||
sort-keys: [0]
|
|
||||||
sort-vars: [0]
|
|
||||||
space-before-blocks: [2, always]
|
|
||||||
space-in-parens: [2, never]
|
|
||||||
space-infix-ops: [2]
|
|
||||||
space-unary-ops: [2]
|
|
||||||
spaced-comment: [2, always]
|
|
||||||
strict: [0]
|
|
||||||
switch-colon-spacing: [2]
|
|
||||||
symbol-description: [2]
|
|
||||||
template-curly-spacing: [2, never]
|
|
||||||
template-tag-spacing: [2, never]
|
|
||||||
unicode-bom: [2, never]
|
|
||||||
unicorn/better-regex: [0]
|
|
||||||
unicorn/catch-error-name: [0]
|
|
||||||
unicorn/consistent-destructuring: [2]
|
|
||||||
unicorn/consistent-function-scoping: [2]
|
|
||||||
unicorn/custom-error-definition: [0]
|
|
||||||
unicorn/empty-brace-spaces: [2]
|
|
||||||
unicorn/error-message: [0]
|
|
||||||
unicorn/escape-case: [0]
|
|
||||||
unicorn/expiring-todo-comments: [0]
|
|
||||||
unicorn/explicit-length-check: [0]
|
|
||||||
unicorn/filename-case: [0]
|
|
||||||
unicorn/import-index: [0]
|
|
||||||
unicorn/import-style: [0]
|
|
||||||
unicorn/new-for-builtins: [2]
|
|
||||||
unicorn/no-abusive-eslint-disable: [0]
|
|
||||||
unicorn/no-array-for-each: [0]
|
|
||||||
unicorn/no-array-instanceof: [0]
|
|
||||||
unicorn/no-array-method-this-argument: [2]
|
|
||||||
unicorn/no-array-push-push: [2]
|
|
||||||
unicorn/no-console-spaces: [0]
|
|
||||||
unicorn/no-document-cookie: [2]
|
|
||||||
unicorn/no-fn-reference-in-iterator: [0]
|
|
||||||
unicorn/no-for-loop: [0]
|
|
||||||
unicorn/no-hex-escape: [0]
|
|
||||||
unicorn/no-keyword-prefix: [0]
|
|
||||||
unicorn/no-lonely-if: [2]
|
|
||||||
unicorn/no-nested-ternary: [0]
|
|
||||||
unicorn/no-new-array: [0]
|
|
||||||
unicorn/no-new-buffer: [0]
|
|
||||||
unicorn/no-null: [0]
|
|
||||||
unicorn/no-object-as-default-parameter: [2]
|
|
||||||
unicorn/no-process-exit: [0]
|
|
||||||
unicorn/no-reduce: [2]
|
|
||||||
unicorn/no-static-only-class: [2]
|
|
||||||
unicorn/no-this-assignment: [2]
|
|
||||||
unicorn/no-unreadable-array-destructuring: [0]
|
|
||||||
unicorn/no-unsafe-regex: [0]
|
|
||||||
unicorn/no-unused-properties: [2]
|
|
||||||
unicorn/no-useless-length-check: [2]
|
|
||||||
unicorn/no-useless-spread: [2]
|
|
||||||
unicorn/no-useless-undefined: [0]
|
|
||||||
unicorn/no-zero-fractions: [2]
|
|
||||||
unicorn/number-literal-case: [0]
|
|
||||||
unicorn/numeric-separators-style: [0]
|
|
||||||
unicorn/prefer-add-event-listener: [2]
|
|
||||||
unicorn/prefer-array-find: [2]
|
|
||||||
unicorn/prefer-array-flat-map: [2]
|
|
||||||
unicorn/prefer-array-flat: [2]
|
|
||||||
unicorn/prefer-array-index-of: [2]
|
|
||||||
unicorn/prefer-array-some: [2]
|
|
||||||
unicorn/prefer-at: [0]
|
|
||||||
unicorn/prefer-dataset: [2]
|
|
||||||
unicorn/prefer-date-now: [2]
|
|
||||||
unicorn/prefer-default-parameters: [0]
|
|
||||||
unicorn/prefer-event-key: [2]
|
|
||||||
unicorn/prefer-includes: [2]
|
|
||||||
unicorn/prefer-math-trunc: [2]
|
|
||||||
unicorn/prefer-modern-dom-apis: [0]
|
|
||||||
unicorn/prefer-module: [2]
|
|
||||||
unicorn/prefer-negative-index: [2]
|
|
||||||
unicorn/prefer-node-append: [0]
|
|
||||||
unicorn/prefer-node-protocol: [0]
|
|
||||||
unicorn/prefer-node-remove: [0]
|
|
||||||
unicorn/prefer-number-properties: [0]
|
|
||||||
unicorn/prefer-object-from-entries: [2]
|
|
||||||
unicorn/prefer-object-has-own: [0]
|
|
||||||
unicorn/prefer-optional-catch-binding: [2]
|
|
||||||
unicorn/prefer-prototype-methods: [0]
|
|
||||||
unicorn/prefer-query-selector: [0]
|
|
||||||
unicorn/prefer-reflect-apply: [0]
|
|
||||||
unicorn/prefer-regexp-test: [2]
|
|
||||||
unicorn/prefer-replace-all: [0]
|
|
||||||
unicorn/prefer-set-has: [0]
|
|
||||||
unicorn/prefer-spread: [0]
|
|
||||||
unicorn/prefer-starts-ends-with: [2]
|
|
||||||
unicorn/prefer-string-slice: [0]
|
|
||||||
unicorn/prefer-switch: [0]
|
|
||||||
unicorn/prefer-ternary: [0]
|
|
||||||
unicorn/prefer-text-content: [2]
|
|
||||||
unicorn/prefer-top-level-await: [0]
|
|
||||||
unicorn/prefer-trim-start-end: [2]
|
|
||||||
unicorn/prefer-type-error: [0]
|
|
||||||
unicorn/prevent-abbreviations: [0]
|
|
||||||
unicorn/require-array-join-separator: [2]
|
|
||||||
unicorn/require-number-to-fixed-digits-argument: [2]
|
|
||||||
unicorn/require-post-message-target-origin: [0]
|
|
||||||
unicorn/string-content: [0]
|
|
||||||
unicorn/throw-new-error: [2]
|
|
||||||
use-isnan: [2]
|
|
||||||
valid-typeof: [2, {requireStringLiterals: true}]
|
|
||||||
vars-on-top: [0]
|
|
||||||
vue/attributes-order: [0]
|
|
||||||
vue/component-definition-name-casing: [0]
|
|
||||||
vue/html-closing-bracket-spacing: [0]
|
|
||||||
vue/max-attributes-per-line: [0]
|
|
||||||
vue/one-component-per-file: [0]
|
|
||||||
wrap-iife: [2, inside]
|
|
||||||
wrap-regex: [0]
|
|
||||||
yield-star-spacing: [2, after]
|
|
||||||
yoda: [2, never]
|
|
|
@ -1,7 +1,6 @@
|
||||||
* text=auto eol=lf
|
conf/* linguist-vendored
|
||||||
/vendor/** -text -eol linguist-vendored
|
docker/* linguist-vendored
|
||||||
/public/vendor/** -text -eol linguist-vendored
|
options/* linguist-vendored
|
||||||
/templates/**/*.tmpl linguist-language=Handlebars
|
public/* linguist-vendored
|
||||||
/.eslintrc linguist-language=YAML
|
scripts/* linguist-vendored
|
||||||
/.stylelintrc linguist-language=YAML
|
templates/* linguist-vendored
|
||||||
/web_src/fomantic/build/** linguist-generated
|
|
|
@ -1,2 +0,0 @@
|
||||||
open_collective: gitea
|
|
||||||
custom: https://www.bountysource.com/teams/gitea
|
|
|
@ -1,21 +1,15 @@
|
||||||
<!-- NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue -->
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
1. Please speak English, this is the language all maintainers can speak and write.
|
1. Please speak English, this is the language all of us can speak and write.
|
||||||
2. Please ask questions or configuration/deploy problems on our Discord
|
2. Please ask questions or configuration/deploy problems on our Discord
|
||||||
server (https://discord.gg/gitea) or forum (https://discourse.gitea.io).
|
server (https://discord.gg/NsatcWJ) or forum (https://discourse.gitea.io).
|
||||||
3. Please take a moment to check that your issue doesn't already exist.
|
3. Please take a moment to check that your issue doesn't already exist.
|
||||||
4. Make sure it's not mentioned in the FAQ (https://docs.gitea.io/en-us/faq)
|
4. Please give all relevant information below for bug reports, because
|
||||||
5. Please give all relevant information below for bug reports, because
|
|
||||||
incomplete details will be handled as an invalid report.
|
incomplete details will be handled as an invalid report.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
- Gitea version (or commit ref):
|
- Gitea version (or commit ref):
|
||||||
- Git version:
|
- Git version:
|
||||||
- Operating system:
|
- Operating system:
|
||||||
<!-- Please include information on whether you built gitea yourself, used one of our downloads or are using some other package -->
|
|
||||||
<!-- Please also tell us how you are running gitea, e.g. if it is being run from docker, a command-line, systemd etc. --->
|
|
||||||
<!-- If you are using a package or systemd tell us what distribution you are using -->
|
|
||||||
- Database (use `[x]`):
|
- Database (use `[x]`):
|
||||||
- [ ] PostgreSQL
|
- [ ] PostgreSQL
|
||||||
- [ ] MySQL
|
- [ ] MySQL
|
||||||
|
@ -24,15 +18,10 @@
|
||||||
- Can you reproduce the bug at https://try.gitea.io:
|
- Can you reproduce the bug at https://try.gitea.io:
|
||||||
- [ ] Yes (provide example URL)
|
- [ ] Yes (provide example URL)
|
||||||
- [ ] No
|
- [ ] No
|
||||||
|
- [ ] Not relevant
|
||||||
- Log gist:
|
- Log gist:
|
||||||
<!-- It really is important to provide pertinent logs -->
|
|
||||||
<!-- Please read https://docs.gitea.io/en-us/logging-configuration/#debugging-problems -->
|
|
||||||
<!-- In addition, if your problem relates to git commands set `RUN_MODE=dev` at the top of app.ini -->
|
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
<!-- If using a proxy or a CDN (e.g. CloudFlare) in front of gitea, please
|
|
||||||
disable the proxy/CDN fully and connect to gitea directly to confirm
|
|
||||||
the issue still persists without those services. -->
|
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
# Configuration for Lock Threads - https://github.com/dessant/lock-threads-app
|
|
||||||
|
|
||||||
# Number of days of inactivity before a closed issue or pull request is locked
|
|
||||||
daysUntilLock: 60
|
|
||||||
|
|
||||||
# Skip issues and pull requests created before a given timestamp. Timestamp must
|
|
||||||
# follow ISO 8601 (`YYYY-MM-DD`). `false` is disabled
|
|
||||||
skipCreatedBefore: false
|
|
||||||
|
|
||||||
# Issues and pull requests with these labels will be ignored.
|
|
||||||
exemptLabels: []
|
|
||||||
|
|
||||||
# Label to add before locking, such as `outdated`. `false` is disabled
|
|
||||||
lockLabel: false
|
|
||||||
|
|
||||||
# Comment to post before locking.
|
|
||||||
lockComment: >
|
|
||||||
This thread has been automatically locked since there has not been
|
|
||||||
any recent activity after it was closed. Please open a new issue for
|
|
||||||
related bugs and link to relevant comments in this thread.
|
|
||||||
|
|
||||||
# Assign `resolved` as the reason for locking. Set to `false` to disable
|
|
||||||
setLockReason: true
|
|
|
@ -1,6 +1,6 @@
|
||||||
Please check the following:
|
Please check the following:
|
||||||
|
|
||||||
1. Make sure you are targeting the `main` branch, pull requests on release branches are only allowed for bug fixes.
|
1. Make sure you are targeting the `master` branch, pull requests on release branches are only allowed for bug fixes.
|
||||||
2. Read contributing guidelines: https://github.com/go-gitea/gitea/blob/master/CONTRIBUTING.md
|
2. Read contributing guidelines: https://github.com/go-gitea/gitea/blob/master/CONTRIBUTING.md
|
||||||
3. Describe what your pull request does and which issue you're targeting (if any)
|
3. Describe what your pull request does and which issue you're targeting (if any)
|
||||||
|
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
# Configuration for probot-stale - https://github.com/probot/stale
|
|
||||||
|
|
||||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
|
||||||
daysUntilStale: 60
|
|
||||||
|
|
||||||
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
|
|
||||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
|
||||||
daysUntilClose: 14
|
|
||||||
|
|
||||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
|
||||||
exemptLabels:
|
|
||||||
- status/blocked
|
|
||||||
- kind/security
|
|
||||||
- lgtm/done
|
|
||||||
- reviewed/confirmed
|
|
||||||
- priority/critical
|
|
||||||
- kind/proposal
|
|
||||||
|
|
||||||
# Set to true to ignore issues in a project (defaults to false)
|
|
||||||
exemptProjects: false
|
|
||||||
|
|
||||||
# Set to true to ignore issues in a milestone (defaults to false)
|
|
||||||
exemptMilestones: false
|
|
||||||
|
|
||||||
# Label to use when marking as stale
|
|
||||||
staleLabel: stale
|
|
||||||
|
|
||||||
# Comment to post when marking as stale. Set to `false` to disable
|
|
||||||
markComment: >
|
|
||||||
This issue has been automatically marked as stale because it has not had recent activity.
|
|
||||||
I am here to help clear issues left open even if solved or waiting for more insight.
|
|
||||||
This issue will be closed if no further activity occurs during the next 2 weeks.
|
|
||||||
If the issue is still valid just add a comment to keep it alive.
|
|
||||||
Thank you for your contributions.
|
|
||||||
|
|
||||||
# Comment to post when closing a stale Issue or Pull Request.
|
|
||||||
closeComment: >
|
|
||||||
This issue has been automatically closed because of inactivity.
|
|
||||||
You can re-open it if needed.
|
|
||||||
|
|
||||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
|
||||||
limitPerRun: 1
|
|
||||||
|
|
||||||
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
|
|
||||||
pulls:
|
|
||||||
daysUntilStale: 60
|
|
||||||
daysUntilClose: 60
|
|
||||||
markComment: >
|
|
||||||
This pull request has been automatically marked as stale because it has not had
|
|
||||||
recent activity. It will be closed if no further activity occurs during the next 2 months. Thank you
|
|
||||||
for your contributions.
|
|
||||||
closeComment: >
|
|
||||||
This pull request has been automatically closed because of inactivity.
|
|
||||||
You can re-open it if needed.
|
|
|
@ -12,7 +12,6 @@ _test
|
||||||
|
|
||||||
# MS VSCode
|
# MS VSCode
|
||||||
.vscode
|
.vscode
|
||||||
__debug_bin
|
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
# Architecture specific extensions/prefixes
|
||||||
*.[568vq]
|
*.[568vq]
|
||||||
|
@ -32,84 +31,31 @@ _testmain.go
|
||||||
|
|
||||||
*coverage.out
|
*coverage.out
|
||||||
coverage.all
|
coverage.all
|
||||||
cpu.out
|
|
||||||
|
|
||||||
/modules/options/bindata.go
|
/modules/options/bindata.go
|
||||||
/modules/options/bindata.go.hash
|
|
||||||
/modules/public/bindata.go
|
/modules/public/bindata.go
|
||||||
/modules/public/bindata.go.hash
|
|
||||||
/modules/templates/bindata.go
|
/modules/templates/bindata.go
|
||||||
/modules/templates/bindata.go.hash
|
|
||||||
|
|
||||||
*.db
|
*.db
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
/gitea
|
/gitea
|
||||||
/gitea-vet
|
|
||||||
/debug
|
/debug
|
||||||
/integrations.test
|
/integrations.test
|
||||||
|
|
||||||
/bin
|
/bin
|
||||||
/dist
|
/dist
|
||||||
/custom/*
|
/custom
|
||||||
!/custom/conf
|
|
||||||
/custom/conf/*
|
|
||||||
!/custom/conf/app.example.ini
|
|
||||||
/data
|
/data
|
||||||
/indexers
|
/indexers
|
||||||
/log
|
/log
|
||||||
/public/img/avatar
|
/public/img/avatar
|
||||||
/integrations/gitea-integration-mysql
|
/integrations/gitea-integration-mysql
|
||||||
/integrations/gitea-integration-mysql8
|
|
||||||
/integrations/gitea-integration-pgsql
|
/integrations/gitea-integration-pgsql
|
||||||
/integrations/gitea-integration-sqlite
|
/integrations/gitea-integration-sqlite
|
||||||
/integrations/gitea-integration-mssql
|
|
||||||
/integrations/indexers-mysql
|
/integrations/indexers-mysql
|
||||||
/integrations/indexers-mysql8
|
|
||||||
/integrations/indexers-pgsql
|
/integrations/indexers-pgsql
|
||||||
/integrations/indexers-sqlite
|
/integrations/indexers-sqlite
|
||||||
/integrations/indexers-mssql
|
|
||||||
/integrations/sqlite.ini
|
|
||||||
/integrations/mysql.ini
|
/integrations/mysql.ini
|
||||||
/integrations/mysql8.ini
|
|
||||||
/integrations/pgsql.ini
|
/integrations/pgsql.ini
|
||||||
/integrations/mssql.ini
|
|
||||||
/node_modules
|
/node_modules
|
||||||
/yarn.lock
|
|
||||||
/yarn-error.log
|
|
||||||
/npm-debug.log*
|
|
||||||
/public/js
|
|
||||||
/public/serviceworker.js
|
|
||||||
/public/css
|
|
||||||
/public/fonts
|
|
||||||
/public/img/webpack
|
|
||||||
/web_src/fomantic/node_modules
|
|
||||||
/web_src/fomantic/build/*
|
|
||||||
!/web_src/fomantic/build/semantic.js
|
|
||||||
!/web_src/fomantic/build/semantic.css
|
|
||||||
!/web_src/fomantic/build/themes
|
|
||||||
/web_src/fomantic/build/themes/*
|
|
||||||
!/web_src/fomantic/build/themes/default
|
|
||||||
/web_src/fomantic/build/themes/default/assets/*
|
|
||||||
!/web_src/fomantic/build/themes/default/assets/fonts
|
|
||||||
/web_src/fomantic/build/themes/default/assets/fonts/*
|
|
||||||
!/web_src/fomantic/build/themes/default/assets/fonts/icons.woff2
|
|
||||||
!/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
|
|
||||||
/VERSION
|
|
||||||
/.air
|
|
||||||
|
|
||||||
# Snapcraft
|
|
||||||
snap/.snapcraft/
|
|
||||||
parts/
|
|
||||||
stage/
|
|
||||||
prime/
|
|
||||||
*.snap
|
|
||||||
*.snap-build
|
|
||||||
*_source.tar.bz2
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Make evidence files
|
|
||||||
/.make_evidence
|
|
||||||
|
|
||||||
# Manpage
|
|
||||||
/man
|
|
||||||
|
|
114
.golangci.yml
114
.golangci.yml
|
@ -1,114 +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 occurred 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/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
|
|
||||||
- path: services/webhook/webhook.go
|
|
||||||
linters:
|
|
||||||
- structcheck
|
|
||||||
- text: "commentFormatting: put a space between `//` and comment text"
|
|
||||||
linters:
|
|
||||||
- gocritic
|
|
||||||
- text: "exitAfterDefer:"
|
|
||||||
linters:
|
|
||||||
- gocritic
|
|
||||||
- path: modules/graceful/manager_windows.go
|
|
||||||
linters:
|
|
||||||
- staticcheck
|
|
||||||
text: "svc.IsAnInteractiveSession is deprecated: Use IsWindowsService instead."
|
|
||||||
|
|
5
.ignore
5
.ignore
|
@ -1,5 +0,0 @@
|
||||||
/vendor
|
|
||||||
/public/vendor/plugins
|
|
||||||
/modules/options/bindata.go
|
|
||||||
/modules/public/bindata.go
|
|
||||||
/modules/templates/bindata.go
|
|
3
.lgtm
3
.lgtm
|
@ -1,3 +1,2 @@
|
||||||
pattern = "(?)LGTM"
|
self_approval_off = false
|
||||||
self_approval_off = true
|
|
||||||
ignore_maintainers_file = true
|
ignore_maintainers_file = true
|
||||||
|
|
5
.npmrc
5
.npmrc
|
@ -1,5 +0,0 @@
|
||||||
audit=false
|
|
||||||
fund=false
|
|
||||||
update-notifier=false
|
|
||||||
package-lock=true
|
|
||||||
save-exact=true
|
|
25
.revive.toml
25
.revive.toml
|
@ -1,25 +0,0 @@
|
||||||
ignoreGeneratedHeader = false
|
|
||||||
severity = "warning"
|
|
||||||
confidence = 0.8
|
|
||||||
errorCode = 1
|
|
||||||
warningCode = 1
|
|
||||||
|
|
||||||
[rule.blank-imports]
|
|
||||||
[rule.context-as-argument]
|
|
||||||
[rule.context-keys-type]
|
|
||||||
[rule.dot-imports]
|
|
||||||
[rule.error-return]
|
|
||||||
[rule.error-strings]
|
|
||||||
[rule.error-naming]
|
|
||||||
[rule.exported]
|
|
||||||
[rule.if-return]
|
|
||||||
[rule.increment-decrement]
|
|
||||||
[rule.var-naming]
|
|
||||||
[rule.var-declaration]
|
|
||||||
[rule.package-comments]
|
|
||||||
[rule.range]
|
|
||||||
[rule.receiver-naming]
|
|
||||||
[rule.time-naming]
|
|
||||||
[rule.unexported-return]
|
|
||||||
[rule.indent-error-flow]
|
|
||||||
[rule.errorf]
|
|
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: double
|
|
||||||
shorthand-property-no-redundant-values: true
|
|
51
BSDmakefile
51
BSDmakefile
|
@ -1,51 +0,0 @@
|
||||||
# GNU makefile proxy script for BSD make
|
|
||||||
# Written and maintained by Mahmoud Al-Qudsi <mqudsi@neosmart.net>
|
|
||||||
# Copyright NeoSmart Technologies <https://neosmart.net/> 2014-2018
|
|
||||||
# Obtain updates from <https://github.com/neosmart/gmake-proxy>
|
|
||||||
#
|
|
||||||
# Redistribution and use in source and binary forms, with or without
|
|
||||||
# modification, are permitted provided that the following conditions are met:
|
|
||||||
#
|
|
||||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
# list of conditions and the following disclaimer.
|
|
||||||
#
|
|
||||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
# this list of conditions and the following disclaimer in the documentation
|
|
||||||
# and/or other materials provided with the distribution.
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
JARG =
|
|
||||||
GMAKE = "gmake"
|
|
||||||
#When gmake is called from another make instance, -w is automatically added
|
|
||||||
#which causes extraneous messages about directory changes to be emitted.
|
|
||||||
#--no-print-directory silences these messages.
|
|
||||||
GARGS = "--no-print-directory"
|
|
||||||
|
|
||||||
.if "$(.MAKE.JOBS)" != ""
|
|
||||||
JARG = -j$(.MAKE.JOBS)
|
|
||||||
.endif
|
|
||||||
|
|
||||||
#by default bmake will cd into ./obj first
|
|
||||||
.OBJDIR: ./
|
|
||||||
|
|
||||||
.PHONY: FRC
|
|
||||||
$(.TARGETS): FRC
|
|
||||||
$(GMAKE) $(GARGS) $(.TARGETS:S,.DONE,,) $(JARG)
|
|
||||||
|
|
||||||
.DONE .DEFAULT: .SILENT
|
|
||||||
$(GMAKE) $(GARGS) $(.TARGETS:S,.DONE,,) $(JARG)
|
|
||||||
|
|
||||||
.ERROR: .SILENT
|
|
||||||
if ! which $(GMAKE) > /dev/null; then \
|
|
||||||
echo "GNU Make is required!"; \
|
|
||||||
fi
|
|
4062
CHANGELOG.md
4062
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
198
CONTRIBUTING.md
198
CONTRIBUTING.md
|
@ -3,24 +3,19 @@
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
- [Contribution Guidelines](#contribution-guidelines)
|
- [Contribution Guidelines](#contribution-guidelines)
|
||||||
- [Table of Contents](#table-of-contents)
|
|
||||||
- [Introduction](#introduction)
|
- [Introduction](#introduction)
|
||||||
- [Bug reports](#bug-reports)
|
- [Bug reports](#bug-reports)
|
||||||
- [Discuss your design](#discuss-your-design)
|
- [Discuss your design](#discuss-your-design)
|
||||||
- [Testing redux](#testing-redux)
|
- [Testing redux](#testing-redux)
|
||||||
- [Vendoring](#vendoring)
|
- [Vendoring](#vendoring)
|
||||||
- [Translation](#translation)
|
- [Translation](#translation)
|
||||||
- [Building Gitea](#building-gitea)
|
|
||||||
- [Code review](#code-review)
|
- [Code review](#code-review)
|
||||||
- [Styleguide](#styleguide)
|
- [Styleguide](#styleguide)
|
||||||
- [Design guideline](#design-guideline)
|
- [Sign your work](#sign-your-work)
|
||||||
- [API v1](#api-v1)
|
|
||||||
- [Developer Certificate of Origin (DCO)](#developer-certificate-of-origin-dco)
|
|
||||||
- [Release Cycle](#release-cycle)
|
- [Release Cycle](#release-cycle)
|
||||||
- [Maintainers](#maintainers)
|
- [Maintainers](#maintainers)
|
||||||
- [Owners](#owners)
|
- [Owners](#owners)
|
||||||
- [Versions](#versions)
|
- [Versions](#versions)
|
||||||
- [Releasing Gitea](#releasing-gitea)
|
|
||||||
- [Copyright](#copyright)
|
- [Copyright](#copyright)
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
@ -53,12 +48,10 @@ getting free help.
|
||||||
|
|
||||||
## Discuss your design
|
## Discuss your design
|
||||||
|
|
||||||
The project welcomes submissions. If you want to change or add something,
|
The project welcomes submissions. If you want to change or add something,
|
||||||
please let everyone know what you're working on—[file an issue](https://github.com/go-gitea/gitea/issues/new)!
|
please let everyone know what you're working on—[file an issue](https://github.com/go-gitea/gitea/issues/new)!
|
||||||
Significant changes must go through the change proposal process
|
Significant changes must go through the [change proposal process](https://github.com/go-gitea/proposals)
|
||||||
before they can be accepted. To create a proposal, file an issue with
|
before they can be accepted.
|
||||||
your proposed changes documented, and make sure to note in the title
|
|
||||||
of the issue that it is a proposal.
|
|
||||||
|
|
||||||
This process gives everyone a chance to validate the design, helps
|
This process gives everyone a chance to validate the design, helps
|
||||||
prevent duplication of effort, and ensures that the idea fits inside
|
prevent duplication of effort, and ensures that the idea fits inside
|
||||||
|
@ -68,31 +61,20 @@ high-level discussions.
|
||||||
|
|
||||||
## Testing redux
|
## Testing redux
|
||||||
|
|
||||||
Before submitting a pull request, run all the tests for the whole tree
|
Before sending code out for review, run all the tests for the
|
||||||
to make sure your changes don't cause regression elsewhere.
|
whole tree to make sure the changes don't break other usage
|
||||||
|
and keep the compatibility on upgrade. To make sure you are
|
||||||
Here's how to run the test suite:
|
running the test suite exactly like we do, you should install
|
||||||
|
the CLI for [Drone CI](https://github.com/drone/drone), as
|
||||||
- code lint
|
we are using the server for continous testing, following [these
|
||||||
|
instructions](http://readme.drone.io/usage/getting-started-cli). After that,
|
||||||
| | |
|
you can simply call `drone exec` within your working directory and it will try
|
||||||
| :-------------------- | :---------------------------------------------------------------- |
|
to run the test suite locally.
|
||||||
|``make lint`` | lint everything (not suggest if you only change one type code) |
|
|
||||||
|``make lint-frontend`` | lint frontend files |
|
|
||||||
|``make lint-backend`` | lint backend files |
|
|
||||||
|
|
||||||
- run test code (Suggest run in linux)
|
|
||||||
|
|
||||||
| | |
|
|
||||||
| :------------------------------------- | :----------------------------------------------- |
|
|
||||||
|``make test[\#TestSpecificName]`` | run unit test |
|
|
||||||
|``make test-sqlite[\#TestSpecificName]``| run [integration](integrations) test for sqlite |
|
|
||||||
|[More detail message about integrations](integrations/README.md) |
|
|
||||||
|
|
||||||
## Vendoring
|
## Vendoring
|
||||||
|
|
||||||
We keep a cached copy of dependencies within the `vendor/` directory,
|
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 [govendor](http://github.com/kardianos/govendor).
|
||||||
|
|
||||||
Pull requests should only include `vendor/` updates if they are part of
|
Pull requests should only include `vendor/` updates if they are part of
|
||||||
the same change, be it a bugfix or a feature addition.
|
the same change, be it a bugfix or a feature addition.
|
||||||
|
@ -101,20 +83,24 @@ The `vendor/` update needs to be justified as part of the PR description,
|
||||||
and must be verified by the reviewers and/or merger to always reference
|
and must be verified by the reviewers and/or merger to always reference
|
||||||
an existing upstream commit.
|
an existing upstream commit.
|
||||||
|
|
||||||
You can find more information on how to get started with it on the [Modules Wiki](https://github.com/golang/go/wiki/Modules).
|
|
||||||
|
|
||||||
## Translation
|
## Translation
|
||||||
|
|
||||||
We do all translation work inside [Crowdin](https://crowdin.com/project/gitea).
|
We do all translation work inside [Crowdin](https://crowdin.com/project/gitea).
|
||||||
The only translation that is maintained in this git repository is
|
The only translation that is maintained in this git repository is
|
||||||
[`en_US.ini`](https://github.com/go-gitea/gitea/blob/master/options/locale/locale_en-US.ini)
|
[`en_US.ini`](https://github.com/go-gitea/gitea/blob/master/options/locale/locale_en-US.ini)
|
||||||
and is synced regularly to Crowdin. Once a translation has reached
|
and is synced regularily to Crowdin. Once a translation has reached
|
||||||
A SATISFACTORY PERCENTAGE it will be synced back into this repo and
|
A SATISFACTORY PERCENTAGE it will be synced back into this repo and
|
||||||
included in the next released version.
|
included in the next released version.
|
||||||
|
|
||||||
## Building Gitea
|
## 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)
|
||||||
|
with `npm` and then run `npm install` and `make stylesheets`.
|
||||||
|
- To build Images: ImageMagick, inkscape and zopflipng binaries must be
|
||||||
|
available in your `PATH` to run `make generate-images`.
|
||||||
|
|
||||||
## Code review
|
## Code review
|
||||||
|
|
||||||
|
@ -123,8 +109,8 @@ makes the change, even if they are an owner or a maintainer. We use GitHub's
|
||||||
pull request workflow to do that. And, we also use [LGTM](http://lgtm.co)
|
pull request workflow to do that. And, we also use [LGTM](http://lgtm.co)
|
||||||
to ensure every PR is reviewed by at least 2 maintainers.
|
to ensure every PR is reviewed by at least 2 maintainers.
|
||||||
|
|
||||||
Please try to make your pull request easy to review for us. And, please read
|
Please try to make your pull request easy to review for us. And, please read
|
||||||
the *[How to get faster PR reviews](https://github.com/kubernetes/community/blob/261cb0fd089b64002c91e8eddceebf032462ccd6/contributors/guide/pull-requests.md#best-practices-for-faster-reviews)* guide;
|
the *[How to get faster PR reviews](https://github.com/kubernetes/community/blob/master/contributors/devel/faster_reviews.md)* guide;
|
||||||
it has lots of useful tips for any project you may want to contribute.
|
it has lots of useful tips for any project you may want to contribute.
|
||||||
Some of the key points:
|
Some of the key points:
|
||||||
|
|
||||||
|
@ -155,98 +141,42 @@ 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:
|
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
|
||||||
- **integration:** Integrations tests
|
have the right to pass it on as an open-source patch. The rules are
|
||||||
- **models:** Contains the data structures used by xorm to construct database tables. It also contains supporting functions to query and update the database. Dependencies to other code in Gitea should be avoided although some modules might be needed (for example for logging).
|
pretty simple: If you can certify [DCO](DCO), then you just add a line
|
||||||
- **models/fixtures:** Sample model data used in integration tests.
|
to every git commit message:
|
||||||
- **models/migrations:** Handling of database migrations between versions. PRs that changes a database structure shall also have a migration step.
|
|
||||||
- **modules:** Different modules to handle specific functionality in Gitea.
|
|
||||||
- **public:** Frontend files (javascript, images, css, etc.)
|
|
||||||
- **routers:** Handling of server requests. As it uses other Gitea packages to serve the request, other packages (models, modules or services) shall not depend on routers
|
|
||||||
- **services:** Support functions for common routing operations. Uses models and modules to handle the request.
|
|
||||||
- **templates:** Golang templates for generating the html output.
|
|
||||||
- **vendor:** External code that Gitea depends on.
|
|
||||||
|
|
||||||
## API v1
|
|
||||||
|
|
||||||
The API is documented by [swagger](http://try.gitea.io/api/swagger) and is based on [GitHub API v3](https://developer.github.com/v3/).
|
|
||||||
Thus, Gitea´s API should use the same endpoints and fields as GitHub´s API as far as possible, unless there are good reasons to deviate.
|
|
||||||
If Gitea provides functionality that GitHub does not, a new endpoint can be created.
|
|
||||||
If information is provided by Gitea that is not provided by the GitHub API, a new field can be used that doesn't collide with any GitHub fields.
|
|
||||||
|
|
||||||
Updating an existing API should not remove existing fields unless there is a really good reason to do so.
|
|
||||||
The same applies to status responses. If you notice a problem, feel free to leave a comment in the code for future refactoring to APIv2 (which is currently not planned).
|
|
||||||
|
|
||||||
All expected results (errors, success, fail messages) should be documented
|
|
||||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L319-L327)).
|
|
||||||
|
|
||||||
All JSON input types must be defined as a struct in [modules/structs/](modules/structs/)
|
|
||||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/modules/structs/issue.go#L76-L91))
|
|
||||||
and referenced in
|
|
||||||
[routers/api/v1/swagger/options.go](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/swagger/options.go).
|
|
||||||
They can then be used like the following:
|
|
||||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L318)).
|
|
||||||
|
|
||||||
All JSON responses must be defined as a struct in [modules/structs/](modules/structs/)
|
|
||||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/modules/structs/issue.go#L36-L68))
|
|
||||||
and referenced in its category in [routers/api/v1/swagger/](routers/api/v1/swagger/)
|
|
||||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/swagger/issue.go#L11-L16))
|
|
||||||
They can be used like the following:
|
|
||||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L277-L279))
|
|
||||||
|
|
||||||
In general, HTTP methods are chosen as follows:
|
|
||||||
* **GET** endpoints return requested object and status **OK (200)**
|
|
||||||
* **DELETE** endpoints return status **No Content (204)**
|
|
||||||
* **POST** endpoints return status **Created (201)**, used to **create** new objects (e.g. a User)
|
|
||||||
* **PUT** endpoints return status **No Content (204)**, used to **add/assign** existing Objects (e.g. User) to something (e.g. Org-Team)
|
|
||||||
* **PATCH** endpoints return changed object and status **OK (200)**, used to **edit/change** an existing object
|
|
||||||
|
|
||||||
|
|
||||||
An endpoint which changes/edits an object expects all fields to be optional (except ones to identify the object, which are required).
|
|
||||||
|
|
||||||
### Endpoints returning lists should
|
|
||||||
* support pagination (`page` & `limit` options in query)
|
|
||||||
* set `X-Total-Count` header via **SetTotalCountHeader** ([example](https://github.com/go-gitea/gitea/blob/7aae98cc5d4113f1e9918b7ee7dd09f67c189e3e/routers/api/v1/repo/issue.go#L444))
|
|
||||||
|
|
||||||
|
|
||||||
## Developer Certificate of Origin (DCO)
|
|
||||||
|
|
||||||
We consider the act of contributing to the code by submitting a Pull
|
|
||||||
Request as the "Sign off" or agreement to the certifications and terms
|
|
||||||
of the [DCO](DCO) and [MIT license](LICENSE). No further action is required.
|
|
||||||
Additionally you could add a line at the end of your commit message.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
Signed-off-by: Joe Smith <joe.smith@email.com>
|
Signed-off-by: Joe Smith <joe.smith@email.com>
|
||||||
```
|
```
|
||||||
|
|
||||||
If you set your `user.name` and `user.email` git configs, you can add the
|
Please use your real name; we really dislike pseudonyms or anonymous
|
||||||
line to the end of your commit automatically with `git commit -s`.
|
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
|
||||||
We assume in good faith that the information you provide is legally binding.
|
commit automatically with `git commit -s`.
|
||||||
|
|
||||||
## Release Cycle
|
## Release Cycle
|
||||||
|
|
||||||
We adopted a release schedule to streamline the process of working
|
We adopted a release schedule to streamline the process of working
|
||||||
on, finishing, and issuing releases. The overall goal is to make a
|
on, finishing, and issuing releases. The overall goal is to make a
|
||||||
minor release every three or four months, which breaks down into two or three months of
|
minor release every two months, which breaks down into one month of
|
||||||
general development followed by one month of testing and polishing
|
general development followed by one month of testing and polishing
|
||||||
known as the release freeze. All the feature pull requests should be
|
known as the release freeze. All the feature pull requests should be
|
||||||
merged before feature freeze. And, during the frozen period, a corresponding
|
merged in the first month of one release period. And, during the frozen
|
||||||
release branch is open for fixes backported from main branch. Release candidates
|
period, a corresponding release branch is open for fixes backported from
|
||||||
are made during this period for user testing to
|
master. Release candidates are made during this period for user testing to
|
||||||
obtain a final version that is maintained in this branch. A release is
|
obtain a final version that is maintained in this branch. A release is
|
||||||
maintained by issuing patch releases to only correct critical problems
|
maintained by issuing patch releases to only correct critical problems
|
||||||
such as crashes or security issues.
|
such as crashes or security issues.
|
||||||
|
|
||||||
Major release cycles are seasonal. They always begin on the 25th and end on
|
Major release cycles are bimonthly. They always begin on the 25th and end on
|
||||||
the 24th (i.e., the 25th of December to March 24th).
|
the 24th (i.e., the 25th of December to February 24th).
|
||||||
|
|
||||||
During a development cycle, we may also publish any necessary minor releases
|
During a development cycle, we may also publish any necessary minor releases
|
||||||
for the previous version. For example, if the latest, published release is
|
for the previous version. For example, if the latest, published release is
|
||||||
v1.2, then minor changes for the previous release—e.g., v1.1.0 -> v1.1.1—are
|
v1.2, then minor changes for the previous release—e.g., v1.1.0 -> v1.1.1—are
|
||||||
still possible.
|
still possible.
|
||||||
|
|
||||||
|
@ -267,10 +197,6 @@ an advisor has time to code review, we will gladly welcome them back
|
||||||
to the maintainers team. If a maintainer is inactive for more than 3
|
to the maintainers team. If a maintainer is inactive for more than 3
|
||||||
months and forgets to leave the maintainers team, the owners may move
|
months and forgets to leave the maintainers team, the owners may move
|
||||||
him or her from the maintainers team to the advisors team.
|
him or her from the maintainers team to the advisors team.
|
||||||
For security reasons, Maintainers should use 2FA for their accounts and
|
|
||||||
if possible provide gpg signed commits.
|
|
||||||
https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/
|
|
||||||
https://help.github.com/articles/signing-commits-with-gpg/
|
|
||||||
|
|
||||||
## Owners
|
## Owners
|
||||||
|
|
||||||
|
@ -281,9 +207,6 @@ be the main owner, and the other two the assistant owners. When the new
|
||||||
owners have been elected, the old owners will give up ownership to the
|
owners have been elected, the old owners will give up ownership to the
|
||||||
newly elected owners. If an owner is unable to do so, the other owners
|
newly elected owners. If an owner is unable to do so, the other owners
|
||||||
will assist in ceding ownership to the newly elected owners.
|
will assist in ceding ownership to the newly elected owners.
|
||||||
For security reasons, Owners or any account with write access (like a bot)
|
|
||||||
must use 2FA.
|
|
||||||
https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/
|
|
||||||
|
|
||||||
After the election, the new owners should proactively agree
|
After the election, the new owners should proactively agree
|
||||||
with our [CONTRIBUTING](CONTRIBUTING.md) requirements in the
|
with our [CONTRIBUTING](CONTRIBUTING.md) requirements in the
|
||||||
|
@ -299,26 +222,6 @@ and lead the development of Gitea.
|
||||||
To honor the past owners, here's the history of the owners and the time
|
To honor the past owners, here's the history of the owners and the time
|
||||||
they served:
|
they served:
|
||||||
|
|
||||||
* 2021-01-01 ~ 2021-12-31 - https://github.com/go-gitea/gitea/issues/13801
|
|
||||||
* [Lunny Xiao](https://gitea.com/lunny) <xiaolunwen@gmail.com>
|
|
||||||
* [Lauris Bukšis-Haberkorns](https://gitea.com/lafriks) <lauris@nix.lv>
|
|
||||||
* [Matti Ranta](https://gitea.com/techknowlogick) <techknowlogick@gitea.io>
|
|
||||||
|
|
||||||
* 2020-01-01 ~ 2020-12-31 - https://github.com/go-gitea/gitea/issues/9230
|
|
||||||
* [Lunny Xiao](https://gitea.com/lunny) <xiaolunwen@gmail.com>
|
|
||||||
* [Lauris Bukšis-Haberkorns](https://gitea.com/lafriks) <lauris@nix.lv>
|
|
||||||
* [Matti Ranta](https://gitea.com/techknowlogick) <techknowlogick@gitea.io>
|
|
||||||
|
|
||||||
* 2019-01-01 ~ 2019-12-31 - https://github.com/go-gitea/gitea/issues/5572
|
|
||||||
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
|
||||||
* [Lauris Bukšis-Haberkorns](https://github.com/lafriks) <lauris@nix.lv>
|
|
||||||
* [Matti Ranta](https://github.com/techknowlogick) <techknowlogick@gitea.io>
|
|
||||||
|
|
||||||
* 2018-01-01 ~ 2018-12-31 - https://github.com/go-gitea/gitea/issues/3255
|
|
||||||
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
|
||||||
* [Lauris Bukšis-Haberkorns](https://github.com/lafriks) <lauris@nix.lv>
|
|
||||||
* [Kim Carlbäcker](https://github.com/bkcsoft) <kim.carlbacker@gmail.com>
|
|
||||||
|
|
||||||
* 2016-11-04 ~ 2017-12-31
|
* 2016-11-04 ~ 2017-12-31
|
||||||
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
* [Lunny Xiao](https://github.com/lunny) <xiaolunwen@gmail.com>
|
||||||
* [Thomas Boerger](https://github.com/tboerger) <thomas@webhippie.de>
|
* [Thomas Boerger](https://github.com/tboerger) <thomas@webhippie.de>
|
||||||
|
@ -337,25 +240,12 @@ in production, please download the latest release tag version. All the
|
||||||
branches will be protected via GitHub, all the PRs to every branch must
|
branches will be protected via GitHub, all the PRs to every branch must
|
||||||
be reviewed by two maintainers and must pass the automatic tests.
|
be reviewed by two maintainers and must pass the automatic tests.
|
||||||
|
|
||||||
## Releasing Gitea
|
|
||||||
|
|
||||||
* Let $vmaj, $vmin and $vpat be Major, Minor and Patch version numbers, $vpat should be rc1, rc2, 0, 1, ...... $vmaj.$vmin will be kept the same as milestones on github or gitea in future.
|
|
||||||
* Before releasing, confirm all the version's milestone issues or PRs has been resolved. Then discuss the release on discord channel #maintainers and get agreed with almost all the owners and mergers. Or you can declare the version and if nobody against in about serval hours.
|
|
||||||
* If this is a big version first you have to create PR for changelog on branch `master` with PRs with label `changelog` and after it has been merged do following steps:
|
|
||||||
* Create `-dev` tag as `git tag -s -F release.notes v$vmaj.$vmin.0-dev` and push the tag as `git push origin v$vmaj.$vmin.0-dev`.
|
|
||||||
* When CI has finished building tag then you have to create a new branch named `release/v$vmaj.$vmin`
|
|
||||||
* If it is bugfix version create PR for changelog on branch `release/v$vmaj.$vmin` and wait till it is reviewed and merged.
|
|
||||||
* Add a tag as `git tag -s -F release.notes v$vmaj.$vmin.$`, release.notes file could be a temporary file to only include the changelog this version which you added to `CHANGELOG.md`.
|
|
||||||
* And then push the tag as `git push origin v$vmaj.$vmin.$`. Drone CI will automatically created a release and upload all the compiled binary. (But currently it didn't add the release notes automatically. Maybe we should fix that.)
|
|
||||||
* If needed send PR for changelog on branch `master`.
|
|
||||||
* Send PR to [blog repository](https://gitea.com/gitea/blog) announcing the release.
|
|
||||||
|
|
||||||
## Copyright
|
## Copyright
|
||||||
|
|
||||||
Code that you contribute should use the standard copyright header:
|
Code that you contribute should use the standard copyright header:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
```
|
```
|
||||||
|
|
54
Dockerfile
54
Dockerfile
|
@ -1,48 +1,21 @@
|
||||||
|
FROM alpine:3.6
|
||||||
|
|
||||||
###################################
|
LABEL maintainer="The Gitea Authors"
|
||||||
#Build stage
|
|
||||||
FROM golang:1.17-alpine3.13 AS build-env
|
|
||||||
|
|
||||||
ARG GOPROXY
|
|
||||||
ENV GOPROXY ${GOPROXY:-direct}
|
|
||||||
|
|
||||||
ARG GITEA_VERSION
|
|
||||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
|
||||||
ENV TAGS "bindata timetzdata $TAGS"
|
|
||||||
ARG CGO_EXTRA_CFLAGS
|
|
||||||
|
|
||||||
#Build deps
|
|
||||||
RUN apk --no-cache add build-base git nodejs npm
|
|
||||||
|
|
||||||
#Setup repo
|
|
||||||
COPY . ${GOPATH}/src/code.gitea.io/gitea
|
|
||||||
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
|
||||||
|
|
||||||
#Checkout version if set
|
|
||||||
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
|
||||||
&& make clean-all build
|
|
||||||
|
|
||||||
# Begin env-to-ini build
|
|
||||||
RUN go build contrib/environment-to-ini/environment-to-ini.go
|
|
||||||
|
|
||||||
FROM alpine:3.13
|
|
||||||
LABEL maintainer="maintainers@gitea.io"
|
|
||||||
|
|
||||||
EXPOSE 22 3000
|
EXPOSE 22 3000
|
||||||
|
|
||||||
RUN apk --no-cache add \
|
RUN apk --no-cache add \
|
||||||
bash \
|
su-exec \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
curl \
|
sqlite \
|
||||||
gettext \
|
bash \
|
||||||
git \
|
git \
|
||||||
linux-pam \
|
linux-pam \
|
||||||
openssh \
|
|
||||||
s6 \
|
s6 \
|
||||||
sqlite \
|
curl \
|
||||||
su-exec \
|
openssh \
|
||||||
gnupg
|
gettext \
|
||||||
|
tzdata
|
||||||
RUN addgroup \
|
RUN addgroup \
|
||||||
-S -g 1000 \
|
-S -g 1000 \
|
||||||
git && \
|
git && \
|
||||||
|
@ -53,17 +26,16 @@ RUN addgroup \
|
||||||
-u 1000 \
|
-u 1000 \
|
||||||
-G git \
|
-G git \
|
||||||
git && \
|
git && \
|
||||||
echo "git:*" | chpasswd -e
|
echo "git:$(dd if=/dev/urandom bs=24 count=1 status=none | base64)" | chpasswd
|
||||||
|
|
||||||
ENV USER git
|
ENV USER git
|
||||||
ENV GITEA_CUSTOM /data/gitea
|
ENV GITEA_CUSTOM /data/gitea
|
||||||
|
ENV GODEBUG=netdns=go
|
||||||
|
|
||||||
VOLUME ["/data"]
|
VOLUME ["/data"]
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/bin/entrypoint"]
|
ENTRYPOINT ["/usr/bin/entrypoint"]
|
||||||
CMD ["/bin/s6-svscan", "/etc/s6"]
|
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
|
COPY gitea /app/gitea/gitea
|
||||||
COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
|
||||||
RUN ln -s /app/gitea/gitea /usr/local/bin/gitea
|
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
FROM multiarch/alpine:aarch64-v3.6
|
||||||
|
|
||||||
|
LABEL maintainer="The Gitea Authors"
|
||||||
|
|
||||||
|
EXPOSE 22 3000
|
||||||
|
|
||||||
|
RUN apk --no-cache add \
|
||||||
|
su-exec \
|
||||||
|
ca-certificates \
|
||||||
|
sqlite \
|
||||||
|
bash \
|
||||||
|
git \
|
||||||
|
linux-pam \
|
||||||
|
s6 \
|
||||||
|
curl \
|
||||||
|
openssh \
|
||||||
|
gettext \
|
||||||
|
tzdata
|
||||||
|
RUN addgroup \
|
||||||
|
-S -g 1000 \
|
||||||
|
git && \
|
||||||
|
adduser \
|
||||||
|
-S -H -D \
|
||||||
|
-h /data/git \
|
||||||
|
-s /bin/bash \
|
||||||
|
-u 1000 \
|
||||||
|
-G git \
|
||||||
|
git && \
|
||||||
|
echo "git:$(dd if=/dev/urandom bs=24 count=1 status=none | base64)" | chpasswd
|
||||||
|
|
||||||
|
ENV USER git
|
||||||
|
ENV GITEA_CUSTOM /data/gitea
|
||||||
|
|
||||||
|
COPY docker /
|
||||||
|
COPY gitea /app/gitea/gitea
|
||||||
|
|
||||||
|
ENV GODEBUG=netdns=go
|
||||||
|
|
||||||
|
VOLUME ["/data"]
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/bin/entrypoint"]
|
||||||
|
CMD ["/bin/s6-svscan", "/etc/s6"]
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
|
|
||||||
###################################
|
|
||||||
#Build stage
|
|
||||||
FROM golang:1.17-alpine3.13 AS build-env
|
|
||||||
|
|
||||||
ARG GOPROXY
|
|
||||||
ENV GOPROXY ${GOPROXY:-direct}
|
|
||||||
|
|
||||||
ARG GITEA_VERSION
|
|
||||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
|
||||||
ENV TAGS "bindata timetzdata $TAGS"
|
|
||||||
ARG CGO_EXTRA_CFLAGS
|
|
||||||
|
|
||||||
#Build deps
|
|
||||||
RUN apk --no-cache add build-base git nodejs npm
|
|
||||||
|
|
||||||
#Setup repo
|
|
||||||
COPY . ${GOPATH}/src/code.gitea.io/gitea
|
|
||||||
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
|
||||||
|
|
||||||
#Checkout version if set
|
|
||||||
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
|
||||||
&& make clean-all build
|
|
||||||
|
|
||||||
# Begin env-to-ini build
|
|
||||||
RUN go build contrib/environment-to-ini/environment-to-ini.go
|
|
||||||
|
|
||||||
FROM alpine:3.13
|
|
||||||
LABEL maintainer="maintainers@gitea.io"
|
|
||||||
|
|
||||||
EXPOSE 2222 3000
|
|
||||||
|
|
||||||
RUN apk --no-cache add \
|
|
||||||
bash \
|
|
||||||
ca-certificates \
|
|
||||||
gettext \
|
|
||||||
git \
|
|
||||||
curl \
|
|
||||||
gnupg
|
|
||||||
|
|
||||||
RUN addgroup \
|
|
||||||
-S -g 1000 \
|
|
||||||
git && \
|
|
||||||
adduser \
|
|
||||||
-S -H -D \
|
|
||||||
-h /var/lib/gitea/git \
|
|
||||||
-s /bin/bash \
|
|
||||||
-u 1000 \
|
|
||||||
-G git \
|
|
||||||
git
|
|
||||||
|
|
||||||
RUN mkdir -p /var/lib/gitea /etc/gitea
|
|
||||||
RUN chown git:git /var/lib/gitea /etc/gitea
|
|
||||||
|
|
||||||
COPY docker/rootless /
|
|
||||||
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /usr/local/bin/gitea
|
|
||||||
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
|
||||||
|
|
||||||
#git:git
|
|
||||||
USER 1000:1000
|
|
||||||
ENV GITEA_WORK_DIR /var/lib/gitea
|
|
||||||
ENV GITEA_CUSTOM /var/lib/gitea/custom
|
|
||||||
ENV GITEA_TEMP /tmp/gitea
|
|
||||||
ENV TMPDIR /tmp/gitea
|
|
||||||
|
|
||||||
#TODO add to docs the ability to define the ini to load (usefull to test and revert a config)
|
|
||||||
ENV GITEA_APP_INI /etc/gitea/app.ini
|
|
||||||
ENV HOME "/var/lib/gitea/git"
|
|
||||||
VOLUME ["/var/lib/gitea", "/etc/gitea"]
|
|
||||||
WORKDIR /var/lib/gitea
|
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
|
||||||
CMD []
|
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
FROM multiarch/alpine:armhf-v3.6
|
||||||
|
|
||||||
|
LABEL maintainer="The Gitea Authors"
|
||||||
|
|
||||||
|
EXPOSE 22 3000
|
||||||
|
|
||||||
|
RUN apk --no-cache add \
|
||||||
|
su-exec \
|
||||||
|
ca-certificates \
|
||||||
|
sqlite \
|
||||||
|
bash \
|
||||||
|
git \
|
||||||
|
linux-pam \
|
||||||
|
s6 \
|
||||||
|
curl \
|
||||||
|
openssh \
|
||||||
|
gettext \
|
||||||
|
tzdata
|
||||||
|
RUN addgroup \
|
||||||
|
-S -g 1000 \
|
||||||
|
git && \
|
||||||
|
adduser \
|
||||||
|
-S -H -D \
|
||||||
|
-h /data/git \
|
||||||
|
-s /bin/bash \
|
||||||
|
-u 1000 \
|
||||||
|
-G git \
|
||||||
|
git && \
|
||||||
|
echo "git:$(dd if=/dev/urandom bs=24 count=1 status=none | base64)" | chpasswd
|
||||||
|
|
||||||
|
ENV USER git
|
||||||
|
ENV GITEA_CUSTOM /data/gitea
|
||||||
|
ENV GODEBUG=netdns=go
|
||||||
|
|
||||||
|
VOLUME ["/data"]
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/bin/entrypoint"]
|
||||||
|
CMD ["/bin/s6-svscan", "/etc/s6"]
|
||||||
|
|
||||||
|
COPY docker /
|
||||||
|
COPY gitea /app/gitea/gitea
|
26
MAINTAINERS
26
MAINTAINERS
|
@ -7,7 +7,6 @@ Kim Carlbäcker <kim.carlbacker@gmail.com> (@bkcsoft)
|
||||||
LefsFlare <nobody@nobody.tld> (@LefsFlarey)
|
LefsFlare <nobody@nobody.tld> (@LefsFlarey)
|
||||||
Lunny Xiao <xiaolunwen@gmail.com> (@lunny)
|
Lunny Xiao <xiaolunwen@gmail.com> (@lunny)
|
||||||
Matthias Loibl <mail@matthiasloibl.com> (@metalmatze)
|
Matthias Loibl <mail@matthiasloibl.com> (@metalmatze)
|
||||||
Morgan Bazalgette <the@howl.moe> (@thehowl)
|
|
||||||
Rachid Zarouali <nobody@nobody.tld> (@xinity)
|
Rachid Zarouali <nobody@nobody.tld> (@xinity)
|
||||||
Rémy Boulanouar <admin@dblk.org> (@DblK)
|
Rémy Boulanouar <admin@dblk.org> (@DblK)
|
||||||
Sandro Santilli <strk@kbt.io> (@strk)
|
Sandro Santilli <strk@kbt.io> (@strk)
|
||||||
|
@ -19,28 +18,3 @@ Lauris Bukšis-Haberkorns <lauris@nix.lv> (@lafriks)
|
||||||
Jonas Östanbäck <jonas.ostanback@gmail.com> (@cez81)
|
Jonas Östanbäck <jonas.ostanback@gmail.com> (@cez81)
|
||||||
David Schneiderbauer <dschneiderbauer@gmail.com> (@daviian)
|
David Schneiderbauer <dschneiderbauer@gmail.com> (@daviian)
|
||||||
Peter Žeby <morlinest@gmail.com> (@morlinest)
|
Peter Žeby <morlinest@gmail.com> (@morlinest)
|
||||||
Matti Ranta <techknowlogick@gitea.io> (@techknowlogick)
|
|
||||||
Jonas Franz <info@jonasfranz.software> (@jonasfranz)
|
|
||||||
Alexey Terentyev <axifnx@gmail.com> (@axifive)
|
|
||||||
Lanre Adelowo <yo@lanre.wtf> (@adelowo)
|
|
||||||
Konrad Langenberg <k@knt.li> (@kolaente)
|
|
||||||
He-Long Zhang <outman99@hotmail.com> (@BetaCat0)
|
|
||||||
Andrew Thornton <art27@cantab.net> (@zeripath)
|
|
||||||
John Olheiser <john.olheiser@gmail.com> (@jolheiser)
|
|
||||||
Richard Mahn <rich.mahn@unfoldingword.org> (@richmahn)
|
|
||||||
Mrsdizzie <info@mrsdizzie.com> (@mrsdizzie)
|
|
||||||
silverwind <me@silverwind.io> (@silverwind)
|
|
||||||
Gary Kim <gary@garykim.dev> (@gary-kim)
|
|
||||||
Guillermo Prandi <gitea.maint@mailfilter.com.ar> (@guillep2k)
|
|
||||||
Mura Li <typeless@ctli.io> (@typeless)
|
|
||||||
6543 <6543@obermui.de> (@6543)
|
|
||||||
jaqra <jaqra@hotmail.com> (@jaqra)
|
|
||||||
David Svantesson <davidsvantesson@gmail.com> (@davidsvantesson)
|
|
||||||
a1012112796 <1012112796@qq.com> (@a1012112796)
|
|
||||||
Karl Heinz Marbaise <kama@soebes.de> (@khmarbaise)
|
|
||||||
Norwin Roosen <git@nroo.de> (@noerw)
|
|
||||||
Kyle Dumont <kdumontnu@gmail.com> (@kdumontnu)
|
|
||||||
Patrick Schratz <patrick.schratz@gmail.com> (@pat-s)
|
|
||||||
Janis Estelmann <admin@oldschoolhack.me> (@KN4CK3R)
|
|
||||||
Steven Kriegler <sk.bunsenbrenner@gmail.com> (@justusbunsi)
|
|
||||||
Jimmy Praet <jimmy.praet@telenet.be> (@jpraet)
|
|
||||||
|
|
806
Makefile
806
Makefile
|
@ -1,733 +1,311 @@
|
||||||
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 := dist
|
||||||
DIST_DIRS := $(DIST)/binaries $(DIST)/release
|
|
||||||
IMPORT := code.gitea.io/gitea
|
IMPORT := code.gitea.io/gitea
|
||||||
export GO111MODULE=on
|
|
||||||
|
|
||||||
GO ?= go
|
GO ?= go
|
||||||
SHASUM ?= shasum -a 256
|
SED_INPLACE := sed -i
|
||||||
HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
|
|
||||||
COMMA := ,
|
|
||||||
|
|
||||||
XGO_VERSION := go-1.17.x
|
|
||||||
MIN_GO_VERSION := 001016000
|
|
||||||
MIN_NODE_VERSION := 012017000
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
ifeq ($(OS), Windows_NT)
|
ifeq ($(OS), Windows_NT)
|
||||||
GOFLAGS := -v -buildmode=exe
|
EXECUTABLE := gitea.exe
|
||||||
EXECUTABLE ?= gitea.exe
|
|
||||||
else ifeq ($(OS), Windows)
|
|
||||||
GOFLAGS := -v -buildmode=exe
|
|
||||||
EXECUTABLE ?= gitea.exe
|
|
||||||
else
|
else
|
||||||
GOFLAGS := -v
|
EXECUTABLE := gitea
|
||||||
EXECUTABLE ?= gitea
|
UNAME_S := $(shell uname -s)
|
||||||
endif
|
ifeq ($(UNAME_S),Darwin)
|
||||||
|
SED_INPLACE := sed -i ''
|
||||||
ifeq ($(shell sed --version 2>/dev/null | grep -q GNU && echo gnu),gnu)
|
endif
|
||||||
SED_INPLACE := sed -i
|
|
||||||
else
|
|
||||||
SED_INPLACE := sed -i ''
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
BINDATA := modules/{options,public,templates}/bindata.go
|
||||||
|
DOCKER_TAG := gitea/gitea:latest
|
||||||
|
GOFILES := $(shell find . -name "*.go" -type f ! -path "./vendor/*" ! -path "*/bindata.go")
|
||||||
GOFMT ?= gofmt -s
|
GOFMT ?= gofmt -s
|
||||||
|
|
||||||
|
GOFLAGS := -i -v
|
||||||
EXTRA_GOFLAGS ?=
|
EXTRA_GOFLAGS ?=
|
||||||
|
|
||||||
MAKE_VERSION := $(shell $(MAKE) -v | head -n 1)
|
LDFLAGS := -X "main.Version=$(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')" -X "main.Tags=$(TAGS)"
|
||||||
MAKE_EVIDENCE_DIR := .make_evidence
|
|
||||||
|
|
||||||
ifeq ($(RACE_ENABLED),true)
|
PACKAGES ?= $(filter-out code.gitea.io/gitea/integrations,$(shell $(GO) list ./... | grep -v /vendor/))
|
||||||
GOFLAGS += -race
|
SOURCES ?= $(shell find . -name "*.go" -type f)
|
||||||
GOTESTFLAGS += -race
|
|
||||||
endif
|
|
||||||
|
|
||||||
STORED_VERSION_FILE := VERSION
|
|
||||||
|
|
||||||
ifneq ($(DRONE_TAG),)
|
|
||||||
VERSION ?= $(subst v,,$(DRONE_TAG))
|
|
||||||
GITEA_VERSION ?= $(VERSION)
|
|
||||||
else
|
|
||||||
ifneq ($(DRONE_BRANCH),)
|
|
||||||
VERSION ?= $(subst release/v,,$(DRONE_BRANCH))
|
|
||||||
else
|
|
||||||
VERSION ?= main
|
|
||||||
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
|
|
||||||
endif
|
|
||||||
|
|
||||||
LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
|
|
||||||
|
|
||||||
LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64
|
|
||||||
|
|
||||||
GO_PACKAGES ?= $(filter-out code.gitea.io/gitea/models/migrations code.gitea.io/gitea/integrations/migration-test code.gitea.io/gitea/integrations,$(shell $(GO) list -mod=vendor ./... | grep -v /vendor/))
|
|
||||||
|
|
||||||
FOMANTIC_WORK_DIR := web_src/fomantic
|
|
||||||
|
|
||||||
WEBPACK_SOURCES := $(shell find web_src/js web_src/less -type f)
|
|
||||||
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
|
|
||||||
|
|
||||||
TAGS ?=
|
TAGS ?=
|
||||||
TAGS_SPLIT := $(subst $(COMMA), ,$(TAGS))
|
|
||||||
TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
|
|
||||||
|
|
||||||
TEST_TAGS ?= sqlite sqlite_unlock_notify
|
TMPDIR := $(shell mktemp -d 2>/dev/null || mktemp -d -t 'gitea-temp')
|
||||||
|
|
||||||
TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMANTIC_WORK_DIR)/node_modules $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR)
|
|
||||||
|
|
||||||
GO_DIRS := cmd integrations models modules routers build services vendor tools
|
|
||||||
|
|
||||||
GO_SOURCES := $(wildcard *.go)
|
|
||||||
GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" -not -path modules/options/bindata.go -not -path modules/public/bindata.go -not -path modules/templates/bindata.go)
|
|
||||||
|
|
||||||
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 \| JSEscape \| Safe}}/api/v1"|g
|
|
||||||
SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|"basePath": "/api/v1"|g
|
|
||||||
SWAGGER_EXCLUDE := code.gitea.io/sdk
|
|
||||||
SWAGGER_NEWLINE_COMMAND := -e '$$a\'
|
|
||||||
|
|
||||||
TEST_MYSQL_HOST ?= mysql:3306
|
TEST_MYSQL_HOST ?= mysql:3306
|
||||||
TEST_MYSQL_DBNAME ?= testgitea
|
TEST_MYSQL_DBNAME ?= testgitea
|
||||||
TEST_MYSQL_USERNAME ?= root
|
TEST_MYSQL_USERNAME ?= root
|
||||||
TEST_MYSQL_PASSWORD ?=
|
TEST_MYSQL_PASSWORD ?=
|
||||||
TEST_MYSQL8_HOST ?= mysql8:3306
|
|
||||||
TEST_MYSQL8_DBNAME ?= testgitea
|
|
||||||
TEST_MYSQL8_USERNAME ?= root
|
|
||||||
TEST_MYSQL8_PASSWORD ?=
|
|
||||||
TEST_PGSQL_HOST ?= pgsql:5432
|
TEST_PGSQL_HOST ?= pgsql:5432
|
||||||
TEST_PGSQL_DBNAME ?= testgitea
|
TEST_PGSQL_DBNAME ?= testgitea
|
||||||
TEST_PGSQL_USERNAME ?= postgres
|
TEST_PGSQL_USERNAME ?= postgres
|
||||||
TEST_PGSQL_PASSWORD ?= postgres
|
TEST_PGSQL_PASSWORD ?= postgres
|
||||||
TEST_PGSQL_SCHEMA ?= gtestschema
|
|
||||||
TEST_MSSQL_HOST ?= mssql:1433
|
ifeq ($(OS), Windows_NT)
|
||||||
TEST_MSSQL_DBNAME ?= gitea
|
EXECUTABLE := gitea.exe
|
||||||
TEST_MSSQL_USERNAME ?= sa
|
else
|
||||||
TEST_MSSQL_PASSWORD ?= MwantsaSecurePassword1
|
EXECUTABLE := gitea
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(DRONE_TAG),)
|
||||||
|
VERSION ?= $(subst v,,$(DRONE_TAG))
|
||||||
|
else
|
||||||
|
ifneq ($(DRONE_BRANCH),)
|
||||||
|
VERSION ?= $(subst release/v,,$(DRONE_BRANCH))
|
||||||
|
else
|
||||||
|
VERSION ?= master
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: build
|
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 " - test test everything"
|
|
||||||
@echo " - test-frontend test frontend files"
|
|
||||||
@echo " - test-backend test 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-manpage generate manpage"
|
|
||||||
@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.16 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 MIN_NODE_VER_FMT := $(shell printf "%g.%g.%g" $(shell echo $(MIN_NODE_VERSION) | grep -o ...)))
|
|
||||||
$(eval NPM_MISSING := $(shell hash npm > /dev/null 2>&1 || echo 1))
|
|
||||||
@if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" -o "$(NPM_MISSING)" = "1" ]; then \
|
|
||||||
echo "Gitea requires Node.js $(MIN_NODE_VER_FMT) 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) node_modules
|
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
$(GO) clean -i ./...
|
$(GO) clean -i ./...
|
||||||
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) $(BINDATA_HASH) \
|
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA) \
|
||||||
integrations*.test \
|
integrations*.test \
|
||||||
integrations/gitea-integration-pgsql/ integrations/gitea-integration-mysql/ integrations/gitea-integration-mysql8/ integrations/gitea-integration-sqlite/ \
|
integrations/gitea-integration-pgsql/ integrations/gitea-integration-mysql/ integrations/gitea-integration-sqlite/ \
|
||||||
integrations/gitea-integration-mssql/ integrations/indexers-mysql/ integrations/indexers-mysql8/ integrations/indexers-pgsql integrations/indexers-sqlite \
|
integrations/indexers-mysql/ integrations/indexers-pgsql integrations/indexers-sqlite \
|
||||||
integrations/indexers-mssql integrations/mysql.ini integrations/mysql8.ini integrations/pgsql.ini integrations/mssql.ini man/
|
integrations/mysql.ini integrations/pgsql.ini
|
||||||
|
|
||||||
.PHONY: fmt
|
.PHONY: fmt
|
||||||
fmt:
|
fmt:
|
||||||
@echo "Running go fmt..."
|
$(GOFMT) -w $(GOFILES)
|
||||||
@$(GOFMT) -w $(GO_SOURCES_OWN)
|
|
||||||
|
|
||||||
.PHONY: vet
|
.PHONY: vet
|
||||||
vet:
|
vet:
|
||||||
@echo "Running go vet..."
|
$(GO) vet $(PACKAGES)
|
||||||
@$(GO) vet $(GO_PACKAGES)
|
|
||||||
@GOOS= GOARCH= $(GO) build -mod=vendor code.gitea.io/gitea-vet
|
|
||||||
@$(GO) vet -vettool=gitea-vet $(GO_PACKAGES)
|
|
||||||
|
|
||||||
.PHONY: $(TAGS_EVIDENCE)
|
.PHONY: generate
|
||||||
$(TAGS_EVIDENCE):
|
generate:
|
||||||
@mkdir -p $(MAKE_EVIDENCE_DIR)
|
@hash go-bindata > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
@echo "$(TAGS)" > $(TAGS_EVIDENCE)
|
$(GO) get -u github.com/jteeuwen/go-bindata/...; \
|
||||||
|
fi
|
||||||
ifneq "$(TAGS)" "$(shell cat $(TAGS_EVIDENCE) 2>/dev/null)"
|
$(GO) generate $(PACKAGES)
|
||||||
TAGS_PREREQ := $(TAGS_EVIDENCE)
|
|
||||||
endif
|
|
||||||
|
|
||||||
.PHONY: generate-swagger
|
.PHONY: generate-swagger
|
||||||
generate-swagger:
|
generate-swagger:
|
||||||
$(SWAGGER) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
|
@hash swagger > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
$(GO) get -u github.com/go-swagger/go-swagger/cmd/swagger; \
|
||||||
$(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
|
|
||||||
|
|
||||||
.PHONY: swagger-check
|
|
||||||
swagger-check: generate-swagger
|
|
||||||
@diff=$$(git diff '$(SWAGGER_SPEC)'); \
|
|
||||||
if [ -n "$$diff" ]; then \
|
|
||||||
echo "Please run 'make generate-swagger' and commit the result:"; \
|
|
||||||
echo "$${diff}"; \
|
|
||||||
exit 1; \
|
|
||||||
fi
|
fi
|
||||||
|
swagger generate spec -o ./public/swagger.v1.json
|
||||||
.PHONY: swagger-validate
|
|
||||||
swagger-validate:
|
|
||||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)'
|
|
||||||
$(SWAGGER) validate './$(SWAGGER_SPEC)'
|
|
||||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
|
||||||
|
|
||||||
.PHONY: errcheck
|
.PHONY: errcheck
|
||||||
errcheck:
|
errcheck:
|
||||||
@hash errcheck > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
@hash errcheck > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
GO111MODULE=off $(GO) get -u github.com/kisielk/errcheck; \
|
$(GO) get -u github.com/kisielk/errcheck; \
|
||||||
fi
|
fi
|
||||||
@echo "Running errcheck..."
|
errcheck $(PACKAGES)
|
||||||
@errcheck $(GO_PACKAGES)
|
|
||||||
|
|
||||||
.PHONY: revive
|
.PHONY: lint
|
||||||
revive:
|
lint:
|
||||||
@hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
@hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
GO111MODULE=off $(GO) get -u github.com/mgechev/revive; \
|
$(GO) get -u github.com/golang/lint/golint; \
|
||||||
fi
|
fi
|
||||||
@revive -config .revive.toml -exclude=./vendor/... ./...
|
for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done;
|
||||||
|
|
||||||
.PHONY: misspell-check
|
.PHONY: misspell-check
|
||||||
misspell-check:
|
misspell-check:
|
||||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
@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
|
fi
|
||||||
@echo "Running misspell-check..."
|
misspell -error -i unknwon $(GOFILES)
|
||||||
@misspell -error -i unknwon $(GO_SOURCES_OWN)
|
|
||||||
|
|
||||||
.PHONY: misspell
|
.PHONY: misspell
|
||||||
misspell:
|
misspell:
|
||||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
@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
|
fi
|
||||||
@echo "Running go misspell..."
|
misspell -w -i unknwon $(GOFILES)
|
||||||
@misspell -w -i unknwon $(GO_SOURCES_OWN)
|
|
||||||
|
|
||||||
.PHONY: fmt-check
|
.PHONY: fmt-check
|
||||||
fmt-check:
|
fmt-check:
|
||||||
# get all go files and run go fmt on them
|
# get all go files and run go fmt on them
|
||||||
@diff=$$($(GOFMT) -d $(GO_SOURCES_OWN)); \
|
@diff=$$($(GOFMT) -d $(GOFILES)); \
|
||||||
if [ -n "$$diff" ]; then \
|
if [ -n "$$diff" ]; then \
|
||||||
echo "Please run 'make fmt' and commit the result:"; \
|
echo "Please run 'make fmt' and commit the result:"; \
|
||||||
echo "$${diff}"; \
|
echo "$${diff}"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi;
|
||||||
|
|
||||||
.PHONY: checks
|
|
||||||
checks: checks-frontend checks-backend
|
|
||||||
|
|
||||||
.PHONY: checks-frontend
|
|
||||||
checks-frontend: 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 --color --max-warnings=0 web_src/js build templates *.config.js
|
|
||||||
npx stylelint --color --max-warnings=0 web_src/less
|
|
||||||
npx editorconfig-checker templates
|
|
||||||
|
|
||||||
.PHONY: lint-backend
|
|
||||||
lint-backend: golangci-lint revive vet
|
|
||||||
|
|
||||||
.PHONY: watch
|
|
||||||
watch:
|
|
||||||
bash tools/watch.sh
|
|
||||||
|
|
||||||
.PHONY: watch-frontend
|
|
||||||
watch-frontend: node-check node_modules
|
|
||||||
rm -rf $(WEBPACK_DEST_ENTRIES)
|
|
||||||
NODE_ENV=development npx webpack --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
|
.PHONY: test
|
||||||
test: test-frontend test-backend
|
test:
|
||||||
|
$(GO) test $(PACKAGES)
|
||||||
.PHONY: test-backend
|
|
||||||
test-backend:
|
|
||||||
@echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
|
|
||||||
@$(GO) test $(GOTESTFLAGS) -mod=vendor -tags='$(TEST_TAGS)' $(GO_PACKAGES)
|
|
||||||
|
|
||||||
.PHONY: test-frontend
|
|
||||||
test-frontend: node_modules
|
|
||||||
@NODE_OPTIONS="--experimental-vm-modules --no-warnings" npx jest --color
|
|
||||||
|
|
||||||
.PHONY: test-check
|
|
||||||
test-check:
|
|
||||||
@echo "Running test-check...";
|
|
||||||
@diff=$$(git status -s); \
|
|
||||||
if [ -n "$$diff" ]; then \
|
|
||||||
echo "make test-backend 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\#%:
|
|
||||||
@echo "Running go test with -tags '$(TEST_TAGS)'..."
|
|
||||||
@$(GO) test -mod=vendor $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -run $(subst .,/,$*) $(GO_PACKAGES)
|
|
||||||
|
|
||||||
.PHONY: coverage
|
.PHONY: coverage
|
||||||
coverage:
|
coverage:
|
||||||
grep '^\(mode: .*\)\|\(.*:[0-9]\+\.[0-9]\+,[0-9]\+\.[0-9]\+ [0-9]\+ [0-9]\+\)$$' coverage.out > coverage-bodged.out
|
@hash gocovmerge > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
grep '^\(mode: .*\)\|\(.*:[0-9]\+\.[0-9]\+,[0-9]\+\.[0-9]\+ [0-9]\+ [0-9]\+\)$$' integration.coverage.out > integration.coverage-bodged.out
|
$(GO) get -u github.com/wadey/gocovmerge; \
|
||||||
GO111MODULE=on $(GO) run -mod=vendor build/gocovmerge.go integration.coverage-bodged.out coverage-bodged.out > coverage.all || (echo "gocovmerge failed"; echo "integration.coverage.out"; cat integration.coverage.out; echo "coverage.out"; cat coverage.out; exit 1)
|
fi
|
||||||
|
echo "mode: set" > coverage.all
|
||||||
|
for PKG in $(PACKAGES); do\
|
||||||
|
egrep "$$PKG[^/]*\.go" integration.coverage.out > int.coverage.out;\
|
||||||
|
gocovmerge $$GOPATH/src/$$PKG/coverage.out int.coverage.out > pkg.coverage.out;\
|
||||||
|
grep -h -v "^mode:" pkg.coverage.out >> coverage.all;\
|
||||||
|
mv pkg.coverage.out $$GOPATH/src/$$PKG/coverage.out;\
|
||||||
|
rm int.coverage.out;\
|
||||||
|
done;
|
||||||
|
|
||||||
.PHONY: unit-test-coverage
|
.PHONY: unit-test-coverage
|
||||||
unit-test-coverage:
|
unit-test-coverage:
|
||||||
@echo "Running unit-test-coverage $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
|
for PKG in $(PACKAGES); do $(GO) test -cover -coverprofile $$GOPATH/src/$$PKG/coverage.out $$PKG || exit 1; done;
|
||||||
@$(GO) test $(GOTESTFLAGS) -mod=vendor -tags='$(TEST_TAGS)' -cover -coverprofile coverage.out $(GO_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
|
|
||||||
|
|
||||||
.PHONY: vendor
|
|
||||||
vendor:
|
|
||||||
$(GO) mod tidy && $(GO) mod vendor
|
|
||||||
|
|
||||||
.PHONY: test-vendor
|
.PHONY: test-vendor
|
||||||
test-vendor: vendor
|
test-vendor:
|
||||||
@diff=$$(git diff vendor/); \
|
@hash govendor > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
if [ -n "$$diff" ]; then \
|
$(GO) get -u github.com/kardianos/govendor; \
|
||||||
echo "Please run 'make vendor' and commit the result:"; \
|
|
||||||
echo "$${diff}"; \
|
|
||||||
exit 1; \
|
|
||||||
fi
|
fi
|
||||||
|
govendor list +unused | tee "$(TMPDIR)/wc-gitea-unused"
|
||||||
|
[ $$(cat "$(TMPDIR)/wc-gitea-unused" | wc -l) -eq 0 ] || echo "Warning: /!\\ Some vendor are not used /!\\"
|
||||||
|
|
||||||
generate-ini-sqlite:
|
govendor list +outside | tee "$(TMPDIR)/wc-gitea-outside"
|
||||||
sed -e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
[ $$(cat "$(TMPDIR)/wc-gitea-outside" | wc -l) -eq 0 ] || exit 1
|
||||||
integrations/sqlite.ini.tmpl > integrations/sqlite.ini
|
|
||||||
|
govendor status || exit 1
|
||||||
|
|
||||||
.PHONY: test-sqlite
|
.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
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test
|
||||||
|
|
||||||
.PHONY: test-sqlite\#%
|
generate-ini:
|
||||||
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 migrations.individual.sqlite.test generate-ini-sqlite
|
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./migrations.sqlite.test
|
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./migrations.individual.sqlite.test
|
|
||||||
|
|
||||||
generate-ini-mysql:
|
|
||||||
sed -e 's|{{TEST_MYSQL_HOST}}|${TEST_MYSQL_HOST}|g' \
|
sed -e 's|{{TEST_MYSQL_HOST}}|${TEST_MYSQL_HOST}|g' \
|
||||||
-e 's|{{TEST_MYSQL_DBNAME}}|${TEST_MYSQL_DBNAME}|g' \
|
-e 's|{{TEST_MYSQL_DBNAME}}|${TEST_MYSQL_DBNAME}|g' \
|
||||||
-e 's|{{TEST_MYSQL_USERNAME}}|${TEST_MYSQL_USERNAME}|g' \
|
-e 's|{{TEST_MYSQL_USERNAME}}|${TEST_MYSQL_USERNAME}|g' \
|
||||||
-e 's|{{TEST_MYSQL_PASSWORD}}|${TEST_MYSQL_PASSWORD}|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
|
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 migrations.individual.mysql.test generate-ini-mysql
|
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./migrations.mysql.test
|
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./migrations.individual.mysql.test
|
|
||||||
|
|
||||||
generate-ini-mysql8:
|
|
||||||
sed -e 's|{{TEST_MYSQL8_HOST}}|${TEST_MYSQL8_HOST}|g' \
|
|
||||||
-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 migrations.individual.mysql8.test generate-ini-mysql8
|
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./migrations.mysql8.test
|
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./migrations.individual.mysql8.test
|
|
||||||
|
|
||||||
generate-ini-pgsql:
|
|
||||||
sed -e 's|{{TEST_PGSQL_HOST}}|${TEST_PGSQL_HOST}|g' \
|
sed -e 's|{{TEST_PGSQL_HOST}}|${TEST_PGSQL_HOST}|g' \
|
||||||
-e 's|{{TEST_PGSQL_DBNAME}}|${TEST_PGSQL_DBNAME}|g' \
|
-e 's|{{TEST_PGSQL_DBNAME}}|${TEST_PGSQL_DBNAME}|g' \
|
||||||
-e 's|{{TEST_PGSQL_USERNAME}}|${TEST_PGSQL_USERNAME}|g' \
|
-e 's|{{TEST_PGSQL_USERNAME}}|${TEST_PGSQL_USERNAME}|g' \
|
||||||
-e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \
|
-e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \
|
||||||
-e 's|{{TEST_PGSQL_SCHEMA}}|${TEST_PGSQL_SCHEMA}|g' \
|
|
||||||
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
|
||||||
integrations/pgsql.ini.tmpl > integrations/pgsql.ini
|
integrations/pgsql.ini.tmpl > integrations/pgsql.ini
|
||||||
|
|
||||||
|
.PHONY: test-mysql
|
||||||
|
test-mysql: integrations.mysql.test generate-ini
|
||||||
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test
|
||||||
|
|
||||||
.PHONY: test-pgsql
|
.PHONY: test-pgsql
|
||||||
test-pgsql: integrations.pgsql.test generate-ini-pgsql
|
test-pgsql: integrations.pgsql.test generate-ini
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test
|
||||||
|
|
||||||
.PHONY: test-pgsql\#%
|
|
||||||
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 migrations.individual.pgsql.test generate-ini-pgsql
|
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./migrations.pgsql.test
|
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./migrations.individual.pgsql.test
|
|
||||||
|
|
||||||
generate-ini-mssql:
|
|
||||||
sed -e 's|{{TEST_MSSQL_HOST}}|${TEST_MSSQL_HOST}|g' \
|
|
||||||
-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-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-mssql-migration
|
|
||||||
test-mssql-migration: migrations.mssql.test migrations.individual.mssql.test generate-ini-mssql
|
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./migrations.mssql.test -test.failfast
|
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./migrations.individual.mssql.test -test.failfast
|
|
||||||
|
|
||||||
.PHONY: bench-sqlite
|
.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 .
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.bench .
|
||||||
|
|
||||||
.PHONY: bench-mysql
|
.PHONY: bench-mysql
|
||||||
bench-mysql: integrations.mysql.test generate-ini-mysql
|
bench-mysql: integrations.mysql.test generate-ini
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test -test.bench .
|
||||||
|
|
||||||
.PHONY: bench-mssql
|
|
||||||
bench-mssql: integrations.mssql.test generate-ini-mssql
|
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
|
||||||
|
|
||||||
.PHONY: bench-pgsql
|
.PHONY: bench-pgsql
|
||||||
bench-pgsql: integrations.pgsql.test generate-ini-pgsql
|
bench-pgsql: integrations.pgsql.test generate-ini
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test -test.bench .
|
||||||
|
|
||||||
|
|
||||||
.PHONY: integration-test-coverage
|
.PHONY: integration-test-coverage
|
||||||
integration-test-coverage: integrations.cover.test generate-ini-mysql
|
integration-test-coverage: integrations.cover.test generate-ini
|
||||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.cover.test -test.coverprofile=integration.coverage.out
|
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.cover.test -test.coverprofile=integration.coverage.out
|
||||||
|
|
||||||
integrations.mysql.test: git-check $(GO_SOURCES)
|
integrations.mysql.test: $(SOURCES)
|
||||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.mysql.test
|
$(GO) test -c code.gitea.io/gitea/integrations -o integrations.mysql.test
|
||||||
|
|
||||||
integrations.mysql8.test: git-check $(GO_SOURCES)
|
integrations.pgsql.test: $(SOURCES)
|
||||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.mysql8.test
|
$(GO) test -c code.gitea.io/gitea/integrations -o integrations.pgsql.test
|
||||||
|
|
||||||
integrations.pgsql.test: git-check $(GO_SOURCES)
|
integrations.sqlite.test: $(SOURCES)
|
||||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.pgsql.test
|
$(GO) test -c code.gitea.io/gitea/integrations -o integrations.sqlite.test -tags 'sqlite'
|
||||||
|
|
||||||
integrations.mssql.test: git-check $(GO_SOURCES)
|
integrations.cover.test: $(SOURCES)
|
||||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.mssql.test
|
$(GO) test -c code.gitea.io/gitea/integrations -coverpkg $(shell echo $(PACKAGES) | tr ' ' ',') -o integrations.cover.test
|
||||||
|
|
||||||
integrations.sqlite.test: git-check $(GO_SOURCES)
|
|
||||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.sqlite.test -tags '$(TEST_TAGS)'
|
|
||||||
|
|
||||||
integrations.cover.test: git-check $(GO_SOURCES)
|
|
||||||
$(GO) test $(GOTESTFLAGS) -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.sqlite.test
|
|
||||||
migrations.sqlite.test: $(GO_SOURCES)
|
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.sqlite.test -tags '$(TEST_TAGS)'
|
|
||||||
|
|
||||||
.PHONY: migrations.individual.mysql.test
|
|
||||||
migrations.individual.mysql.test: $(GO_SOURCES)
|
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/models/migrations -o migrations.individual.mysql.test
|
|
||||||
|
|
||||||
.PHONY: migrations.individual.mysql8.test
|
|
||||||
migrations.individual.mysql8.test: $(GO_SOURCES)
|
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/models/migrations -o migrations.individual.mysql8.test
|
|
||||||
|
|
||||||
.PHONY: migrations.individual.pgsql.test
|
|
||||||
migrations.individual.pgsql.test: $(GO_SOURCES)
|
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/models/migrations -o migrations.individual.pgsql.test
|
|
||||||
|
|
||||||
.PHONY: migrations.individual.mssql.test
|
|
||||||
migrations.individual.mssql.test: $(GO_SOURCES)
|
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/models/migrations -o migrations.individual.mssql.test
|
|
||||||
|
|
||||||
.PHONY: migrations.individual.sqlite.test
|
|
||||||
migrations.individual.sqlite.test: $(GO_SOURCES)
|
|
||||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/models/migrations -o migrations.individual.sqlite.test -tags '$(TEST_TAGS)'
|
|
||||||
|
|
||||||
.PHONY: check
|
.PHONY: check
|
||||||
check: test
|
check: test
|
||||||
|
|
||||||
.PHONY: install $(TAGS_PREREQ)
|
.PHONY: install
|
||||||
install: $(wildcard *.go)
|
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
|
.PHONY: build
|
||||||
build: frontend backend
|
build: $(EXECUTABLE)
|
||||||
|
|
||||||
.PHONY: frontend
|
$(EXECUTABLE): $(SOURCES)
|
||||||
frontend: $(WEBPACK_DEST)
|
$(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||||
|
|
||||||
.PHONY: backend
|
.PHONY: docker
|
||||||
backend: go-check generate $(EXECUTABLE)
|
docker:
|
||||||
|
docker run -ti --rm -v $(CURDIR):/srv/app/src/code.gitea.io/gitea -w /srv/app/src/code.gitea.io/gitea -e TAGS="bindata $(TAGS)" webhippie/golang:edge make clean generate build
|
||||||
.PHONY: generate
|
docker build -t $(DOCKER_TAG) .
|
||||||
generate: $(TAGS_PREREQ)
|
|
||||||
@echo "Running go generate..."
|
|
||||||
@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 $@
|
|
||||||
|
|
||||||
.PHONY: release
|
.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-check
|
||||||
|
|
||||||
$(DIST_DIRS):
|
.PHONY: release-dirs
|
||||||
mkdir -p $(DIST_DIRS)
|
release-dirs:
|
||||||
|
mkdir -p $(DIST)/binaries $(DIST)/release
|
||||||
|
|
||||||
.PHONY: release-windows
|
.PHONY: release-windows
|
||||||
release-windows: | $(DIST_DIRS)
|
release-windows:
|
||||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
$(GO) get -u github.com/karalabe/xgo; \
|
||||||
fi
|
fi
|
||||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||||
ifeq (,$(findstring gogit,$(TAGS)))
|
|
||||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo gogit $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
|
|
||||||
endif
|
|
||||||
ifeq ($(CI),drone)
|
ifeq ($(CI),drone)
|
||||||
cp /build/* $(DIST)/binaries
|
mv /build/* $(DIST)/binaries
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: release-linux
|
.PHONY: release-linux
|
||||||
release-linux: | $(DIST_DIRS)
|
release-linux:
|
||||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
$(GO) get -u github.com/karalabe/xgo; \
|
||||||
fi
|
fi
|
||||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
|
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/*' -out gitea-$(VERSION) .
|
||||||
ifeq ($(CI),drone)
|
ifeq ($(CI),drone)
|
||||||
cp /build/* $(DIST)/binaries
|
mv /build/* $(DIST)/binaries
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: release-darwin
|
.PHONY: release-darwin
|
||||||
release-darwin: | $(DIST_DIRS)
|
release-darwin:
|
||||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
$(GO) get -u github.com/karalabe/xgo; \
|
||||||
fi
|
fi
|
||||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
|
xgo -dest $(DIST)/binaries -tags 'netgo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/*' -out gitea-$(VERSION) .
|
||||||
ifeq ($(CI),drone)
|
ifeq ($(CI),drone)
|
||||||
cp /build/* $(DIST)/binaries
|
mv /build/* $(DIST)/binaries
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: release-copy
|
.PHONY: release-copy
|
||||||
release-copy: | $(DIST_DIRS)
|
release-copy:
|
||||||
cd $(DIST); for file in `find /build -type f -name "*"`; do cp $${file} ./release/; done;
|
$(foreach file,$(wildcard $(DIST)/binaries/$(EXECUTABLE)-*),cp $(file) $(DIST)/release/$(notdir $(file));)
|
||||||
|
|
||||||
.PHONY: release-check
|
.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;
|
cd $(DIST)/release; $(foreach file,$(wildcard $(DIST)/release/$(EXECUTABLE)-*),sha256sum $(notdir $(file)) > $(notdir $(file)).sha256;)
|
||||||
|
|
||||||
.PHONY: release-compress
|
.PHONY: javascripts
|
||||||
release-compress: | $(DIST_DIRS)
|
javascripts: public/js/index.js
|
||||||
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
|
||||||
GO111MODULE=off $(GO) get -u github.com/ulikunitz/xz/cmd/gxz; \
|
|
||||||
fi
|
|
||||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
|
|
||||||
|
|
||||||
.PHONY: release-sources
|
.IGNORE: public/js/index.js
|
||||||
release-sources: | $(DIST_DIRS)
|
public/js/index.js: $(JAVASCRIPTS)
|
||||||
echo $(VERSION) > $(STORED_VERSION_FILE)
|
cat $< >| $@
|
||||||
# bsdtar needs a ^ to prevent matching subdirectories
|
|
||||||
$(eval EXCL := --exclude=$(shell tar --help | grep -q bsdtar && echo "^")./)
|
|
||||||
tar $(addprefix $(EXCL),$(TAR_EXCLUDES)) -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz .
|
|
||||||
rm -f $(STORED_VERSION_FILE)
|
|
||||||
|
|
||||||
.PHONY: release-docs
|
.PHONY: stylesheets-check
|
||||||
release-docs: | $(DIST_DIRS) docs
|
stylesheets-check: generate-stylesheets
|
||||||
tar -czf $(DIST)/release/gitea-docs-$(VERSION).tar.gz -C ./docs/public .
|
@diff=$$(git diff public/css/index.css); \
|
||||||
|
|
||||||
.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:
|
|
||||||
rm -rf $(FOMANTIC_WORK_DIR)/build
|
|
||||||
cd $(FOMANTIC_WORK_DIR) && npm install --no-save
|
|
||||||
cp -f $(FOMANTIC_WORK_DIR)/theme.config.less $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/theme.config
|
|
||||||
cp -rf $(FOMANTIC_WORK_DIR)/_site $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/
|
|
||||||
cp -f web_src/js/vendor/dropdown.js $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/definitions/modules
|
|
||||||
cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build
|
|
||||||
rm -f $(FOMANTIC_WORK_DIR)/build/*.min.*
|
|
||||||
|
|
||||||
.PHONY: webpack
|
|
||||||
webpack: $(WEBPACK_DEST)
|
|
||||||
|
|
||||||
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
|
|
||||||
@$(MAKE) -s node-check node_modules
|
|
||||||
rm -rf $(WEBPACK_DEST_ENTRIES)
|
|
||||||
npx webpack
|
|
||||||
@touch $(WEBPACK_DEST)
|
|
||||||
|
|
||||||
.PHONY: svg
|
|
||||||
svg: node-check | node_modules
|
|
||||||
rm -rf $(SVG_DEST_DIR)
|
|
||||||
node build/generate-svg.js
|
|
||||||
|
|
||||||
.PHONY: svg-check
|
|
||||||
svg-check: svg
|
|
||||||
@git add $(SVG_DEST_DIR)
|
|
||||||
@diff=$$(git diff --cached $(SVG_DEST_DIR)); \
|
|
||||||
if [ -n "$$diff" ]; then \
|
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}"; \
|
echo "$${diff}"; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi;
|
||||||
|
|
||||||
|
.PHONY: generate-stylesheets
|
||||||
|
generate-stylesheets:
|
||||||
|
node_modules/.bin/lessc --no-ie-compat --clean-css public/less/index.less public/css/index.css
|
||||||
|
|
||||||
|
.PHONY: swagger-ui
|
||||||
|
swagger-ui:
|
||||||
|
rm -Rf public/vendor/assets/swagger-ui
|
||||||
|
git clone --depth=10 -b v3.3.2 --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
|
.PHONY: update-translations
|
||||||
update-translations:
|
update-translations:
|
||||||
|
@ -739,47 +317,27 @@ update-translations:
|
||||||
mv ./translations/*.ini ./options/locale/
|
mv ./translations/*.ini ./options/locale/
|
||||||
rmdir ./translations
|
rmdir ./translations
|
||||||
|
|
||||||
.PHONY: generate-license
|
|
||||||
generate-license:
|
|
||||||
GO111MODULE=on $(GO) run build/generate-licenses.go
|
|
||||||
|
|
||||||
.PHONY: generate-gitignore
|
|
||||||
generate-gitignore:
|
|
||||||
GO111MODULE=on $(GO) run build/generate-gitignores.go
|
|
||||||
|
|
||||||
.PHONY: generate-images
|
.PHONY: generate-images
|
||||||
generate-images: | node_modules
|
generate-images:
|
||||||
npm install --no-save --no-package-lock fabric@4 imagemin-zopfli@7
|
mkdir -p $(TMPDIR)/images
|
||||||
node build/generate-images.js $(TAGS)
|
inkscape -f $(PWD)/assets/logo.svg -w 880 -h 880 -e $(PWD)/public/img/gitea-lg.png
|
||||||
|
inkscape -f $(PWD)/assets/logo.svg -w 120 -h 120 -jC -i layer1 -e $(TMPDIR)/images/sm-1.png
|
||||||
.PHONY: generate-manpage
|
inkscape -f $(PWD)/assets/logo.svg -w 120 -h 120 -jC -i layer2 -e $(TMPDIR)/images/sm-2.png
|
||||||
generate-manpage:
|
composite -compose atop $(TMPDIR)/images/sm-2.png $(TMPDIR)/images/sm-1.png $(PWD)/public/img/gitea-sm.png
|
||||||
@[ -f gitea ] || make backend
|
inkscape -f $(PWD)/assets/logo.svg -w 200 -h 200 -e $(PWD)/public/img/avatar_default.png
|
||||||
@mkdir -p man/man1/ man/man5
|
inkscape -f $(PWD)/assets/logo.svg -w 180 -h 180 -e $(PWD)/public/img/favicon.png
|
||||||
@./gitea docs --man > man/man1/gitea.1
|
inkscape -f $(PWD)/assets/logo.svg -w 128 -h 128 -e $(TMPDIR)/images/128-raw.png
|
||||||
@gzip -9 man/man1/gitea.1 && echo man/man1/gitea.1.gz created
|
inkscape -f $(PWD)/assets/logo.svg -w 64 -h 64 -e $(TMPDIR)/images/64-raw.png
|
||||||
@#TODO A smal script witch format config-cheat-sheet.en-us.md nicely to suit as config man page
|
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
|
||||||
.PHONY: pr\#%
|
composite -compose atop $(TMPDIR)/images/32-2.png $(TMPDIR)/images/32-1.png $(TMPDIR)/images/32-raw.png
|
||||||
pr\#%: clean-all
|
inkscape -f $(PWD)/assets/logo.svg -w 16 -h 16 -jC -i layer1 -e $(TMPDIR)/images/16-raw.png
|
||||||
$(GO) run contrib/pr/checkout.go $*
|
zopflipng $(TMPDIR)/images/128-raw.png $(TMPDIR)/images/128.png
|
||||||
|
zopflipng $(TMPDIR)/images/64-raw.png $(TMPDIR)/images/64.png
|
||||||
.PHONY: golangci-lint
|
zopflipng $(TMPDIR)/images/32-raw.png $(TMPDIR)/images/32.png
|
||||||
golangci-lint:
|
zopflipng $(TMPDIR)/images/16-raw.png $(TMPDIR)/images/16.png
|
||||||
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
rm -f $(TMPDIR)/images/*-*.png
|
||||||
export BINARY="golangci-lint"; \
|
convert $(TMPDIR)/images/16.png $(TMPDIR)/images/32.png \
|
||||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.37.0; \
|
$(TMPDIR)/images/64.png $(TMPDIR)/images/128.png \
|
||||||
fi
|
$(PWD)/public/img/favicon.ico
|
||||||
golangci-lint run --timeout 10m
|
rm -rf $(TMPDIR)/images
|
||||||
|
|
||||||
.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
|
|
||||||
|
|
165
README.md
165
README.md
|
@ -1,52 +1,22 @@
|
||||||
<p align="center">
|
[简体中文](https://github.com/go-gitea/gitea/blob/master/README_ZH.md)
|
||||||
<a href="https://gitea.io/">
|
|
||||||
<img alt="Gitea" src="https://raw.githubusercontent.com/go-gitea/gitea/main/public/img/gitea.svg" width="220"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<h1 align="center">Gitea - Git with a cup of tea</h1>
|
|
||||||
|
|
||||||
<p align="center">
|
# Gitea - Git with a cup of tea
|
||||||
<a href="https://drone.gitea.io/go-gitea/gitea" title="Build Status">
|
|
||||||
<img src="https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg?ref=refs/heads/main">
|
|
||||||
</a>
|
|
||||||
<a href="https://discord.gg/Gitea" title="Join the Discord chat at https://discord.gg/Gitea">
|
|
||||||
<img src="https://img.shields.io/discord/322538954119184384.svg">
|
|
||||||
</a>
|
|
||||||
<a href="https://codecov.io/gh/go-gitea/gitea" title="Codecov">
|
|
||||||
<img src="https://codecov.io/gh/go-gitea/gitea/branch/main/graph/badge.svg">
|
|
||||||
</a>
|
|
||||||
<a href="https://goreportcard.com/report/code.gitea.io/gitea" title="Go Report Card">
|
|
||||||
<img src="https://goreportcard.com/badge/code.gitea.io/gitea">
|
|
||||||
</a>
|
|
||||||
<a href="https://godoc.org/code.gitea.io/gitea" title="GoDoc">
|
|
||||||
<img src="https://godoc.org/code.gitea.io/gitea?status.svg">
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/go-gitea/gitea/releases/latest" title="GitHub release">
|
|
||||||
<img src="https://img.shields.io/github/release/go-gitea/gitea.svg">
|
|
||||||
</a>
|
|
||||||
<a href="https://www.codetriage.com/go-gitea/gitea" title="Help Contribute to Open Source">
|
|
||||||
<img src="https://www.codetriage.com/go-gitea/gitea/badges/users.svg">
|
|
||||||
</a>
|
|
||||||
<a href="https://opencollective.com/gitea" title="Become a backer/sponsor of gitea">
|
|
||||||
<img src="https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen">
|
|
||||||
</a>
|
|
||||||
<a href="https://opensource.org/licenses/MIT" title="License: MIT">
|
|
||||||
<img src="https://img.shields.io/badge/License-MIT-blue.svg">
|
|
||||||
</a>
|
|
||||||
<a href="https://crowdin.com/project/gitea" title="Crowdin">
|
|
||||||
<img src="https://badges.crowdin.net/gitea/localized.svg">
|
|
||||||
</a>
|
|
||||||
<a href="https://www.tickgit.com/browse?repo=github.com/go-gitea/gitea" title="TODOs">
|
|
||||||
<img src="https://badgen.net/https/api.tickgit.com/badgen/github.com/go-gitea/gitea">
|
|
||||||
</a>
|
|
||||||
<a href="https://www.bountysource.com/teams/gitea" title="Bountysource">
|
|
||||||
<img src="https://img.shields.io/bountysource/team/gitea/activity">
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p align="center">
|
[![Build Status](https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg)](https://drone.gitea.io/go-gitea/gitea)
|
||||||
<a href="README_ZH.md">View the chinese version of this document</a>
|
[![Join the Discord chat at https://discord.gg/NsatcWJ](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/NsatcWJ)
|
||||||
</p>
|
[![Join the Matrix chat at https://matrix.to/#/#gitea:matrix.org](https://img.shields.io/badge/matrix-%23gitea%3Amatrix.org-7bc9a4.svg)](https://matrix.to/#/#gitea:matrix.org)
|
||||||
|
[![](https://images.microbadger.com/badges/image/gitea/gitea.svg)](https://microbadger.com/images/gitea/gitea "Get your own image badge on microbadger.com")
|
||||||
|
[![codecov](https://codecov.io/gh/go-gitea/gitea/branch/master/graph/badge.svg)](https://codecov.io/gh/go-gitea/gitea)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/code.gitea.io/gitea)
|
||||||
|
[![GoDoc](https://godoc.org/code.gitea.io/gitea?status.svg)](https://godoc.org/code.gitea.io/gitea)
|
||||||
|
[![Release](https://github-release-version.herokuapp.com/github/go-gitea/gitea/release.svg?style=flat)](https://github.com/go-gitea/gitea/releases/latest)
|
||||||
|
|
||||||
|
| | | |
|
||||||
|
|:---:|:---:|:---:|
|
||||||
|
|![Dashboard](https://image.ibb.co/dms6DG/1.png)|![Repository](https://image.ibb.co/m6MSLw/2.png)|![Commits History](https://image.ibb.co/cjrSLw/3.png)|
|
||||||
|
|![Branches](https://image.ibb.co/e6vbDG/4.png)|![Issues](https://image.ibb.co/bJTJSb/5.png)|![Pull Request View](https://image.ibb.co/e02dSb/6.png)|
|
||||||
|
|![Releases](https://image.ibb.co/cUzgfw/7.png)|![Activity](https://image.ibb.co/eZgGDG/8.png)|![Wiki](https://image.ibb.co/dYV9YG/9.png)|
|
||||||
|
|![Diff](https://image.ibb.co/ewA9YG/10.png)|![Organization](https://image.ibb.co/ceOwDG/11.png)|![Profile](https://image.ibb.co/c44Q7b/12.png)|
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
|
@ -61,61 +31,24 @@ This project has been
|
||||||
[forked](https://blog.gitea.io/2016/12/welcome-to-gitea/) from
|
[forked](https://blog.gitea.io/2016/12/welcome-to-gitea/) from
|
||||||
[Gogs](https://gogs.io) since 2016.11 but changed a lot.
|
[Gogs](https://gogs.io) since 2016.11 but changed a lot.
|
||||||
|
|
||||||
## Building
|
## Notes
|
||||||
|
|
||||||
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.16](https://golang.org/dl/) or greater.
|
|
||||||
- `make frontend` which requires [Node.js 12.17](https://nodejs.org/en/download/) or greater and Internet connectivity to download npm dependencies.
|
|
||||||
|
|
||||||
When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js and Internet connectivity.
|
|
||||||
|
|
||||||
Parallelism (`make -j <num>`) is not supported.
|
|
||||||
|
|
||||||
More info: https://docs.gitea.io/en-us/install-from-source/
|
|
||||||
|
|
||||||
## Using
|
|
||||||
|
|
||||||
./gitea web
|
|
||||||
|
|
||||||
NOTE: If you're interested in using our APIs, we have experimental
|
|
||||||
support with [documentation](https://try.gitea.io/api/swagger).
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Expected workflow is: Fork -> Patch -> Push -> Pull Request
|
|
||||||
|
|
||||||
NOTES:
|
|
||||||
|
|
||||||
1. **YOU MUST READ THE [CONTRIBUTORS GUIDE](CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST.**
|
1. **YOU MUST READ THE [CONTRIBUTORS GUIDE](CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST.**
|
||||||
2. If you have found a vulnerability in the project, please write privately to **security@gitea.io**. Thanks!
|
2. If you have found a vulnerability in the project, please write privately to **security@gitea.io**. Thanks!
|
||||||
|
3. If you're interested in using our APIs, we have experimental support with [documentation](https://godoc.org/code.gitea.io/sdk/gitea).
|
||||||
|
|
||||||
## Translating
|
## Docs
|
||||||
|
|
||||||
Translations are done through Crowdin. If you want to translate to a new language ask one of the managers in the Crowdin project to add a new language there.
|
For more information and instructions about how to install Gitea, please look
|
||||||
|
at our [documentation](https://docs.gitea.io/en-us/). If you have questions
|
||||||
|
that are not covered by the documentation, you can get in contact with us on
|
||||||
|
our [Discord server](https://discord.gg/NsatcWJ),
|
||||||
|
[Matrix room](https://matrix.to/#/#gitea:matrix.org),
|
||||||
|
or [forum](https://discourse.gitea.io/)!
|
||||||
|
|
||||||
You can also just create an issue for adding a language or ask on discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty but we hope fo fill it as questions pop up.
|
## Contributing
|
||||||
|
|
||||||
https://docs.gitea.io/en-us/translation-guidelines/
|
Fork -> Patch -> Push -> Pull Request
|
||||||
|
|
||||||
[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://crowdin.com/project/gitea)
|
|
||||||
|
|
||||||
## Further information
|
|
||||||
|
|
||||||
For more information and instructions about how to install Gitea, please look at our [documentation](https://docs.gitea.io/en-us/).
|
|
||||||
If you have questions that are not covered by the documentation, you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create a post in the [discourse forum](https://discourse.gitea.io/).
|
|
||||||
|
|
||||||
We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea).
|
|
||||||
The hugo-based documentation theme is hosted at [gitea/theme](https://gitea.com/gitea/theme).
|
|
||||||
The official Gitea CLI is developed at [gitea/tea](https://gitea.com/gitea/tea).
|
|
||||||
|
|
||||||
## Authors
|
## Authors
|
||||||
|
|
||||||
|
@ -123,48 +56,8 @@ The official Gitea CLI is developed at [gitea/tea](https://gitea.com/gitea/tea).
|
||||||
* [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
|
* [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
|
||||||
* [Translators](options/locale/TRANSLATORS)
|
* [Translators](options/locale/TRANSLATORS)
|
||||||
|
|
||||||
## Backers
|
|
||||||
|
|
||||||
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/gitea#backer)]
|
|
||||||
|
|
||||||
<a href="https://opencollective.com/gitea#backers" target="_blank"><img src="https://opencollective.com/gitea/backers.svg?width=890"></a>
|
|
||||||
|
|
||||||
## Sponsors
|
|
||||||
|
|
||||||
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/gitea#sponsor)]
|
|
||||||
|
|
||||||
<a href="https://opencollective.com/gitea/sponsor/0/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/0/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/gitea/sponsor/1/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/1/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/gitea/sponsor/2/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/2/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/gitea/sponsor/3/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/3/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/gitea/sponsor/4/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/4/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/gitea/sponsor/5/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/5/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/gitea/sponsor/6/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/6/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/gitea/sponsor/7/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/7/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/gitea/sponsor/8/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/8/avatar.svg"></a>
|
|
||||||
<a href="https://opencollective.com/gitea/sponsor/9/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/9/avatar.svg"></a>
|
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
**How do you pronounce Gitea?**
|
|
||||||
|
|
||||||
Gitea is pronounced [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY) as in "gi-tea" with a hard g.
|
|
||||||
|
|
||||||
**Why is this not hosted on a Gitea instance?**
|
|
||||||
|
|
||||||
We're [working on it](https://github.com/go-gitea/gitea/issues/1029).
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the MIT License.
|
This project is licensed under the MIT License.
|
||||||
See the [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) file
|
See the [LICENSE](https://github.com/go-gitea/gitea/blob/master/LICENSE) file
|
||||||
for the full license text.
|
for the full license text.
|
||||||
|
|
||||||
## Screenshots
|
|
||||||
Looking for an overview of the interface? Check it out!
|
|
||||||
|
|
||||||
|![Dashboard](https://dl.gitea.io/screenshots/home_timeline.png)|![User Profile](https://dl.gitea.io/screenshots/user_profile.png)|![Global Issues](https://dl.gitea.io/screenshots/global_issues.png)|
|
|
||||||
|:---:|:---:|:---:|
|
|
||||||
|![Branches](https://dl.gitea.io/screenshots/branches.png)|![Web Editor](https://dl.gitea.io/screenshots/web_editor.png)|![Activity](https://dl.gitea.io/screenshots/activity.png)|
|
|
||||||
|![New Migration](https://dl.gitea.io/screenshots/migration.png)|![Migrating](https://dl.gitea.io/screenshots/migration.gif)|![Pull Request View](https://image.ibb.co/e02dSb/6.png)
|
|
||||||
![Pull Request Dark](https://dl.gitea.io/screenshots/pull_requests_dark.png)|![Diff Review Dark](https://dl.gitea.io/screenshots/review_dark.png)|![Diff Dark](https://dl.gitea.io/screenshots/diff_dark.png)|
|
|
||||||
|
|
82
README_ZH.md
82
README_ZH.md
|
@ -1,56 +1,25 @@
|
||||||
<p align="center">
|
[English](https://github.com/go-gitea/gitea/blob/master/README.md)
|
||||||
<a href="https://gitea.io/">
|
|
||||||
<img alt="Gitea" src="https://raw.githubusercontent.com/go-gitea/gitea/main/public/img/gitea.svg" width="220"/>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<h1 align="center">Gitea - Git with a cup of tea</h1>
|
|
||||||
|
|
||||||
<p align="center">
|
# Gitea - Git with a cup of tea
|
||||||
<a href="https://drone.gitea.io/go-gitea/gitea" title="Build Status">
|
|
||||||
<img src="https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg?ref=refs/heads/main">
|
|
||||||
</a>
|
|
||||||
<a href="https://discord.gg/Gitea" title="Join the Discord chat at https://discord.gg/Gitea">
|
|
||||||
<img src="https://img.shields.io/discord/322538954119184384.svg">
|
|
||||||
</a>
|
|
||||||
<a href="https://codecov.io/gh/go-gitea/gitea" title="Codecov">
|
|
||||||
<img src="https://codecov.io/gh/go-gitea/gitea/branch/main/graph/badge.svg">
|
|
||||||
</a>
|
|
||||||
<a href="https://goreportcard.com/report/code.gitea.io/gitea" title="Go Report Card">
|
|
||||||
<img src="https://goreportcard.com/badge/code.gitea.io/gitea">
|
|
||||||
</a>
|
|
||||||
<a href="https://godoc.org/code.gitea.io/gitea" title="GoDoc">
|
|
||||||
<img src="https://godoc.org/code.gitea.io/gitea?status.svg">
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/go-gitea/gitea/releases/latest" title="GitHub release">
|
|
||||||
<img src="https://img.shields.io/github/release/go-gitea/gitea.svg">
|
|
||||||
</a>
|
|
||||||
<a href="https://www.codetriage.com/go-gitea/gitea" title="Help Contribute to Open Source">
|
|
||||||
<img src="https://www.codetriage.com/go-gitea/gitea/badges/users.svg">
|
|
||||||
</a>
|
|
||||||
<a href="https://opencollective.com/gitea" title="Become a backer/sponsor of gitea">
|
|
||||||
<img src="https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen">
|
|
||||||
</a>
|
|
||||||
<a href="https://opensource.org/licenses/MIT" title="License: MIT">
|
|
||||||
<img src="https://img.shields.io/badge/License-MIT-blue.svg">
|
|
||||||
</a>
|
|
||||||
<a href="https://crowdin.com/project/gitea" title="Crowdin">
|
|
||||||
<img src="https://badges.crowdin.net/gitea/localized.svg">
|
|
||||||
</a>
|
|
||||||
<a href="https://www.tickgit.com/browse?repo=github.com/go-gitea/gitea" title="TODOs">
|
|
||||||
<img src="https://badgen.net/https/api.tickgit.com/badgen/github.com/go-gitea/gitea">
|
|
||||||
</a>
|
|
||||||
<a href="https://img.shields.io/bountysource/team/gitea" title="Bountysource">
|
|
||||||
<img src="https://img.shields.io/bountysource/team/gitea/activity">
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p align="center">
|
[![Build Status](https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg)](https://drone.gitea.io/go-gitea/gitea)
|
||||||
<a href="README.md">View the english version of this document</a>
|
[![Join the chat at https://img.shields.io/discord/322538954119184384.svg](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/NsatcWJ)
|
||||||
</p>
|
[![](https://images.microbadger.com/badges/image/gitea/gitea.svg)](https://microbadger.com/images/gitea/gitea "Get your own image badge on microbadger.com")
|
||||||
|
[![Coverage Status](https://coverage.gitea.io/badges/go-gitea/gitea/coverage.svg)](https://coverage.gitea.io/go-gitea/gitea)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/code.gitea.io/gitea)
|
||||||
|
[![GoDoc](https://godoc.org/code.gitea.io/gitea?status.svg)](https://godoc.org/code.gitea.io/gitea)
|
||||||
|
[![Release](https://github-release-version.herokuapp.com/github/go-gitea/gitea/release.svg?style=flat)](https://github.com/go-gitea/gitea/releases/latest)
|
||||||
|
|
||||||
|
| | | |
|
||||||
|
|:---:|:---:|:---:|
|
||||||
|
|![Dashboard](https://image.ibb.co/dms6DG/1.png)|![Repository](https://image.ibb.co/m6MSLw/2.png)|![Commits History](https://image.ibb.co/cjrSLw/3.png)|
|
||||||
|
|![Branches](https://image.ibb.co/e6vbDG/4.png)|![Issues](https://image.ibb.co/bJTJSb/5.png)|![Pull Request View](https://image.ibb.co/e02dSb/6.png)|
|
||||||
|
|![Releases](https://image.ibb.co/cUzgfw/7.png)|![Activity](https://image.ibb.co/eZgGDG/8.png)|![Wiki](https://image.ibb.co/dYV9YG/9.png)|
|
||||||
|
|![Diff](https://image.ibb.co/ewA9YG/10.png)|![Organization](https://image.ibb.co/ceOwDG/11.png)|![Profile](https://image.ibb.co/c44Q7b/12.png)|
|
||||||
|
|
||||||
## 目标
|
## 目标
|
||||||
|
|
||||||
Gitea 的首要目标是创建一个极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。我们采用 Go 作为后端语言,这使我们只要生成一个可执行程序即可。并且他还支持跨平台,支持 Linux, macOS 和 Windows 以及各种架构,除了 x86,amd64,还包括 ARM 和 PowerPC。
|
Gitea的首要目标是创建一个极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。我们采用Go作为后端语言,这使我们只要生成一个可执行程序即可。并且他还支持跨平台,支持 Linux, macOS 和 Windows 以及各种架构,除了x86,amd64,还包括 ARM 和 PowerPC。
|
||||||
|
|
||||||
如果您想试用一下,请访问 [在线Demo](https://try.gitea.io/)!
|
如果您想试用一下,请访问 [在线Demo](https://try.gitea.io/)!
|
||||||
|
|
||||||
|
@ -62,17 +31,12 @@ 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 来和我们交流。
|
||||||
|
|
||||||
## 贡献流程
|
## 贡献流程
|
||||||
|
|
||||||
Fork -> Patch -> Push -> Pull Request
|
Fork -> Patch -> Push -> Pull Request
|
||||||
|
|
||||||
## 翻译
|
|
||||||
|
|
||||||
多语言翻译是基于Crowdin进行的.
|
|
||||||
[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://crowdin.com/project/gitea)
|
|
||||||
|
|
||||||
## 作者
|
## 作者
|
||||||
|
|
||||||
* [Maintainers](https://github.com/orgs/go-gitea/people)
|
* [Maintainers](https://github.com/orgs/go-gitea/people)
|
||||||
|
@ -81,12 +45,4 @@ Fork -> Patch -> Push -> Pull Request
|
||||||
|
|
||||||
## 授权许可
|
## 授权许可
|
||||||
|
|
||||||
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件中。
|
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/go-gitea/gitea/blob/master/LICENSE) 文件中。
|
||||||
|
|
||||||
## 截图
|
|
||||||
|
|
||||||
|![Dashboard](https://dl.gitea.io/screenshots/home_timeline.png)|![User Profile](https://dl.gitea.io/screenshots/user_profile.png)|![Global Issues](https://dl.gitea.io/screenshots/global_issues.png)|
|
|
||||||
|:---:|:---:|:---:|
|
|
||||||
|![Branches](https://dl.gitea.io/screenshots/branches.png)|![Web Editor](https://dl.gitea.io/screenshots/web_editor.png)|![Activity](https://dl.gitea.io/screenshots/activity.png)|
|
|
||||||
|![New Migration](https://dl.gitea.io/screenshots/migration.png)|![Migrating](https://dl.gitea.io/screenshots/migration.gif)|![Pull Request View](https://image.ibb.co/e02dSb/6.png)
|
|
||||||
![Pull Request Dark](https://dl.gitea.io/screenshots/pull_requests_dark.png)|![Diff Review Dark](https://dl.gitea.io/screenshots/review_dark.png)|![Diff Dark](https://dl.gitea.io/screenshots/diff_dark.png)|
|
|
||||||
|
|
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
189
assets/logo.svg
189
assets/logo.svg
|
@ -1,31 +1,160 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg version="1.1" id="main_outline" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
y="0px" viewBox="0 0 640 640" style="enable-background:new 0 0 640 640;" xml:space="preserve">
|
|
||||||
<g>
|
<svg
|
||||||
<path id="teabag" style="fill:#FFFFFF" d="M395.9,484.2l-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5,21.2-17.9,33.8-11.8
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
c17.2,8.3,27.1,13,27.1,13l-0.1-109.2l16.7-0.1l0.1,117.1c0,0,57.4,24.2,83.1,40.1c3.7,2.3,10.2,6.8,12.9,14.4
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
c2.1,6.1,2,13.1-1,19.3l-61,126.9C423.6,484.9,408.4,490.3,395.9,484.2z"/>
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
<g>
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
<g>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<path style="fill:#609926" d="M622.7,149.8c-4.1-4.1-9.6-4-9.6-4s-117.2,6.6-177.9,8c-13.3,0.3-26.5,0.6-39.6,0.7c0,39.1,0,78.2,0,117.2
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
c-5.5-2.6-11.1-5.3-16.6-7.9c0-36.4-0.1-109.2-0.1-109.2c-29,0.4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
c-9.8-0.6-22.5-2.1-39,1.5c-8.7,1.8-33.5,7.4-53.8,26.9C-4.9,212.4,6.6,276.2,8,285.8c1.7,11.7,6.9,44.2,31.7,72.5
|
width="512"
|
||||||
c45.8,56.1,144.4,54.8,144.4,54.8s12.1,28.9,30.6,55.5c25,33.1,50.7,58.9,75.7,62c63,0,188.9-0.1,188.9-0.1s12,0.1,28.3-10.3
|
height="512"
|
||||||
c14-8.5,26.5-23.4,26.5-23.4s12.9-13.8,30.9-45.3c5.5-9.7,10.1-19.1,14.1-28c0,0,55.2-117.1,55.2-231.1
|
viewBox="0 0 135.46667 135.46667"
|
||||||
C633.2,157.9,624.7,151.8,622.7,149.8z M125.6,353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6,321.8,60,295.4
|
version="1.1"
|
||||||
c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5,38.5-30c13.8-3.7,31-3.1,31-3.1s7.1,59.4,15.7,94.2c7.2,29.2,24.8,77.7,24.8,77.7
|
id="svg8"
|
||||||
S142.5,359.9,125.6,353.9z M425.9,461.5c0,0-6.1,14.5-19.6,15.4c-5.8,0.4-10.3-1.2-10.3-1.2s-0.3-0.1-5.3-2.1l-112.9-55
|
sodipodi:docname="logo.svg"
|
||||||
c0,0-10.9-5.7-12.8-15.6c-2.2-8.1,2.7-18.1,2.7-18.1L322,273c0,0,4.8-9.7,12.2-13c0.6-0.3,2.3-1,4.5-1.5c8.1-2.1,18,2.8,18,2.8
|
inkscape:version="0.92.1 r15371"
|
||||||
l110.7,53.7c0,0,12.6,5.7,15.3,16.2c1.9,7.4-0.5,14-1.8,17.2C474.6,363.8,425.9,461.5,425.9,461.5z"/>
|
inkscape:export-filename=""
|
||||||
<path style="fill:#609926" d="M326.8,380.1c-8.2,0.1-15.4,5.8-17.3,13.8c-1.9,8,2,16.3,9.1,20c7.7,4,17.5,1.8,22.7-5.4
|
inkscape:export-xdpi="48.000004"
|
||||||
c5.1-7.1,4.3-16.9-1.8-23.1l24-49.1c1.5,0.1,3.7,0.2,6.2-0.5c4.1-0.9,7.1-3.6,7.1-3.6c4.2,1.8,8.6,3.8,13.2,6.1
|
inkscape:export-ydpi="48.000004">
|
||||||
c4.8,2.4,9.3,4.9,13.4,7.3c0.9,0.5,1.8,1.1,2.8,1.9c1.6,1.3,3.4,3.1,4.7,5.5c1.9,5.5-1.9,14.9-1.9,14.9
|
<defs
|
||||||
c-2.3,7.6-18.4,40.6-18.4,40.6c-8.1-0.2-15.3,5-17.7,12.5c-2.6,8.1,1.1,17.3,8.9,21.3c7.8,4,17.4,1.7,22.5-5.3
|
id="defs2" />
|
||||||
c5-6.8,4.6-16.3-1.1-22.6c1.9-3.7,3.7-7.4,5.6-11.3c5-10.4,13.5-30.4,13.5-30.4c0.9-1.7,5.7-10.3,2.7-21.3
|
<sodipodi:namedview
|
||||||
c-2.5-11.4-12.6-16.7-12.6-16.7c-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3c4.7-9.7,9.4-19.3,14.1-29
|
id="base"
|
||||||
c-4.1-2-8.1-4-12.2-6.1c-4.8,9.8-9.7,19.7-14.5,29.5c-6.7-0.1-12.9,3.5-16.1,9.4c-3.4,6.3-2.7,14.1,1.9,19.8
|
pagecolor="#ffffff"
|
||||||
C343.2,346.5,335,363.3,326.8,380.1z"/>
|
bordercolor="#666666"
|
||||||
</g>
|
borderopacity="1.0"
|
||||||
</g>
|
inkscape:pageopacity="0"
|
||||||
</g>
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="0.70710678"
|
||||||
|
inkscape:cx="418.13805"
|
||||||
|
inkscape:cy="177.57445"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer2"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
width="256px"
|
||||||
|
showguides="false"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1137"
|
||||||
|
inkscape:window-x="1912"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:pagecheckerboard="false"
|
||||||
|
inkscape:measure-start="283.373,243.952"
|
||||||
|
inkscape:measure-end="290.267,236.527">
|
||||||
|
<sodipodi:guide
|
||||||
|
position="0,0"
|
||||||
|
orientation="0,512"
|
||||||
|
id="guide3699"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="135.46667,0"
|
||||||
|
orientation="-512,0"
|
||||||
|
id="guide3701"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="135.46667,135.46667"
|
||||||
|
orientation="0,-512"
|
||||||
|
id="guide3703"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="0,135.46667"
|
||||||
|
orientation="512,0"
|
||||||
|
id="guide3705"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-161.53334)"
|
||||||
|
style="display:inline">
|
||||||
|
<path
|
||||||
|
style="fill:#609926;fill-opacity:1;stroke:#428f29;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
d="m 27.709937,195.15095 c -9.546573,-0.0272 -22.3392732,6.79805 -21.6317552,23.90397 1.105534,26.72889 25.4565952,29.20839 35.1916502,29.42301 1.068023,5.01357 12.521798,22.30563 21.001818,23.21667 h 37.15277 c 22.27763,-1.66785 38.9607,-75.75671 26.59321,-76.03825 -46.781583,2.47691 -49.995146,2.13838 -88.599758,0 -2.495053,-0.0266 -5.972321,-0.49474 -9.707935,-0.5054 z m 2.491319,9.45886 c 1.351378,13.69267 3.555849,21.70359 8.018216,33.94345 -11.382872,-1.50473 -21.069822,-5.22443 -22.851515,-19.10984 -0.950962,-7.4112 2.390428,-15.16769 14.833299,-14.83361 z"
|
||||||
|
id="path3722"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="sscccccsccsc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer2"
|
||||||
|
inkscape:label="Layer 2"
|
||||||
|
style="display:inline">
|
||||||
|
<rect
|
||||||
|
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.24757317;stroke-opacity:1"
|
||||||
|
id="rect4599"
|
||||||
|
width="34.762054"
|
||||||
|
height="34.762054"
|
||||||
|
x="87.508659"
|
||||||
|
y="18.291576"
|
||||||
|
transform="rotate(25.914715)"
|
||||||
|
ry="5.4825778" />
|
||||||
|
<path
|
||||||
|
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26644793px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 79.804947,57.359056 3.241146,1.609954 V 35.255731 h -3.262698 z"
|
||||||
|
id="path4525"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccc" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer3"
|
||||||
|
inkscape:label="Layer 3"
|
||||||
|
style="display:inline">
|
||||||
|
<g
|
||||||
|
style="display:inline"
|
||||||
|
id="g4539">
|
||||||
|
<circle
|
||||||
|
transform="rotate(-19.796137)"
|
||||||
|
r="3.4745038"
|
||||||
|
cy="90.077766"
|
||||||
|
cx="49.064713"
|
||||||
|
id="path4606"
|
||||||
|
style="fill:#609926;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" />
|
||||||
|
<circle
|
||||||
|
transform="rotate(-19.796137)"
|
||||||
|
r="3.4745038"
|
||||||
|
cy="102.1049"
|
||||||
|
cx="36.810425"
|
||||||
|
id="path4606-3"
|
||||||
|
style="fill:#609926;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" />
|
||||||
|
<circle
|
||||||
|
transform="rotate(-19.796137)"
|
||||||
|
r="3.4745038"
|
||||||
|
cy="111.43928"
|
||||||
|
cx="46.484283"
|
||||||
|
id="path4606-1"
|
||||||
|
style="fill:#609926;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" />
|
||||||
|
<rect
|
||||||
|
transform="rotate(26.024158)"
|
||||||
|
y="18.061695"
|
||||||
|
x="97.333458"
|
||||||
|
height="27.261492"
|
||||||
|
width="2.6726954"
|
||||||
|
id="rect4629-8"
|
||||||
|
style="fill:#609926;fill-opacity:1;stroke:none;stroke-width:0.27444693;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
sodipodi:nodetypes="cc"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4514"
|
||||||
|
d="m 76.558096,68.116343 c 12.97589,6.395378 13.012989,4.101862 4.890858,20.907244"
|
||||||
|
style="fill:none;stroke:#609926;stroke-width:2.68000007;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 5.6 KiB |
25
build.go
25
build.go
|
@ -1,25 +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.
|
|
||||||
|
|
||||||
//go:build vendor
|
|
||||||
// +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 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,87 +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.
|
|
||||||
|
|
||||||
//go:build ignore
|
|
||||||
// +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,223 +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.
|
|
||||||
|
|
||||||
//go:build ignore
|
|
||||||
// +build ignore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"go/format"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"regexp"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"unicode/utf8"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
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 modifier 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,131 +0,0 @@
|
||||||
//go:build ignore
|
|
||||||
// +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-gitignore"
|
|
||||||
url = "https://api.github.com/repos/github/gitignore/tarball"
|
|
||||||
githubApiToken = ""
|
|
||||||
githubUsername = ""
|
|
||||||
destination = ""
|
|
||||||
)
|
|
||||||
|
|
||||||
flag.StringVar(&destination, "dest", "options/gitignore/", "destination for the gitignores")
|
|
||||||
flag.StringVar(&githubUsername, "username", "", "github username")
|
|
||||||
flag.StringVar(&githubApiToken, "token", "", "github api token")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
filesToCopy := make(map[string]string, 0)
|
|
||||||
|
|
||||||
for {
|
|
||||||
hdr, err := tr.Next()
|
|
||||||
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to iterate archive. %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if filepath.Ext(hdr.Name) != ".gitignore" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if hdr.Typeflag == tar.TypeSymlink {
|
|
||||||
fmt.Printf("Found symlink %s -> %s\n", hdr.Name, hdr.Linkname)
|
|
||||||
filesToCopy[strings.TrimSuffix(filepath.Base(hdr.Name), ".gitignore")] = strings.TrimSuffix(filepath.Base(hdr.Linkname), ".gitignore")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := os.Create(path.Join(destination, strings.TrimSuffix(filepath.Base(hdr.Name), ".gitignore")))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to create new file. %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for dst, src := range filesToCopy {
|
|
||||||
// Read all content of src to data
|
|
||||||
src = path.Join(destination, src)
|
|
||||||
data, err := ioutil.ReadFile(src)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to read src file. %s", err)
|
|
||||||
}
|
|
||||||
// Write data to dst
|
|
||||||
dst = path.Join(destination, dst)
|
|
||||||
err = ioutil.WriteFile(dst, data, 0644)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to write new file. %s", err)
|
|
||||||
}
|
|
||||||
fmt.Printf("Written (copy of %s) %s\n", src, dst)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Done")
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
import imageminZopfli from 'imagemin-zopfli';
|
|
||||||
import {optimize, extendDefaultPlugins} from 'svgo';
|
|
||||||
import {fabric} from 'fabric';
|
|
||||||
import fs from 'fs';
|
|
||||||
import {resolve, dirname} from 'path';
|
|
||||||
import {fileURLToPath} from 'url';
|
|
||||||
|
|
||||||
const {readFile, writeFile} = fs.promises;
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
||||||
const logoFile = resolve(__dirname, '../assets/logo.svg');
|
|
||||||
|
|
||||||
function exit(err) {
|
|
||||||
if (err) console.error(err);
|
|
||||||
process.exit(err ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadSvg(svg) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
fabric.loadSVGFromString(svg, (objects, options) => {
|
|
||||||
resolve({objects, options});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function generate(svg, outputFile, {size, bg}) {
|
|
||||||
if (outputFile.endsWith('.svg')) {
|
|
||||||
const {data} = optimize(svg, {
|
|
||||||
plugins: extendDefaultPlugins([
|
|
||||||
'removeDimensions',
|
|
||||||
{
|
|
||||||
name: 'addAttributesToSVGElement',
|
|
||||||
params: {attributes: [{width: size}, {height: size}]}
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
});
|
|
||||||
await writeFile(outputFile, data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {objects, options} = await loadSvg(svg);
|
|
||||||
const canvas = new fabric.Canvas();
|
|
||||||
canvas.setDimensions({width: size, height: size});
|
|
||||||
const ctx = canvas.getContext('2d');
|
|
||||||
ctx.scale(options.width ? (size / options.width) : 1, options.height ? (size / options.height) : 1);
|
|
||||||
|
|
||||||
if (bg) {
|
|
||||||
canvas.add(new fabric.Rect({
|
|
||||||
left: 0,
|
|
||||||
top: 0,
|
|
||||||
height: size * (1 / (size / options.height)),
|
|
||||||
width: size * (1 / (size / options.width)),
|
|
||||||
fill: 'white',
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas.add(fabric.util.groupSVGElements(objects, options));
|
|
||||||
canvas.renderAll();
|
|
||||||
|
|
||||||
let png = Buffer.from([]);
|
|
||||||
for await (const chunk of canvas.createPNGStream()) {
|
|
||||||
png = Buffer.concat([png, chunk]);
|
|
||||||
}
|
|
||||||
|
|
||||||
png = await imageminZopfli({more: true})(png);
|
|
||||||
await writeFile(outputFile, png);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const gitea = process.argv.slice(2).includes('gitea');
|
|
||||||
const svg = await readFile(logoFile, 'utf8');
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
generate(svg, resolve(__dirname, '../public/img/logo.svg'), {size: 32}),
|
|
||||||
generate(svg, resolve(__dirname, '../public/img/logo.png'), {size: 512}),
|
|
||||||
generate(svg, resolve(__dirname, '../public/img/favicon.png'), {size: 180}),
|
|
||||||
generate(svg, resolve(__dirname, '../public/img/avatar_default.png'), {size: 200}),
|
|
||||||
generate(svg, resolve(__dirname, '../public/img/apple-touch-icon.png'), {size: 180, bg: true}),
|
|
||||||
gitea && generate(svg, resolve(__dirname, '../public/img/gitea.svg'), {size: 32}),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
main().then(exit).catch(exit);
|
|
||||||
|
|
|
@ -1,119 +0,0 @@
|
||||||
//go:build ignore
|
|
||||||
// +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,57 +0,0 @@
|
||||||
import fastGlob from 'fast-glob';
|
|
||||||
import {optimize} from 'svgo';
|
|
||||||
import {resolve, parse, dirname} from 'path';
|
|
||||||
import fs from 'fs';
|
|
||||||
import {fileURLToPath} from 'url';
|
|
||||||
|
|
||||||
const {readFile, writeFile, mkdir} = fs.promises;
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
||||||
const glob = (pattern) => fastGlob.sync(pattern, {cwd: resolve(__dirname), absolute: true});
|
|
||||||
const outputDir = resolve(__dirname, '../public/img/svg');
|
|
||||||
|
|
||||||
function exit(err) {
|
|
||||||
if (err) console.error(err);
|
|
||||||
process.exit(err ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function processFile(file, {prefix, fullName} = {}) {
|
|
||||||
let name;
|
|
||||||
|
|
||||||
if (fullName) {
|
|
||||||
name = fullName;
|
|
||||||
} else {
|
|
||||||
name = parse(file).name;
|
|
||||||
if (prefix) name = `${prefix}-${name}`;
|
|
||||||
if (prefix === 'octicon') name = name.replace(/-[0-9]+$/, ''); // chop of '-16' on octicons
|
|
||||||
}
|
|
||||||
|
|
||||||
const {data} = optimize(await readFile(file, 'utf8'), {
|
|
||||||
plugins: [
|
|
||||||
{name: 'preset-default'},
|
|
||||||
{name: 'removeXMLNS'},
|
|
||||||
{name: 'removeDimensions'},
|
|
||||||
{name: 'addClassesToSVGElement', params: {classNames: ['svg', name]}},
|
|
||||||
{name: 'addAttributesToSVGElement', params: {attributes: [{'width': '16'}, {'height': '16'}, {'aria-hidden': 'true'}]}},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
await writeFile(resolve(outputDir, `${name}.svg`), data);
|
|
||||||
}
|
|
||||||
|
|
||||||
function processFiles(pattern, opts) {
|
|
||||||
return glob(pattern).map((file) => processFile(file, opts));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
try {
|
|
||||||
await mkdir(outputDir);
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
...processFiles('../node_modules/@primer/octicons/build/svg/*-16.svg', {prefix: 'octicon'}),
|
|
||||||
...processFiles('../web_src/svg/*.svg'),
|
|
||||||
...processFiles('../public/img/gitea.svg', {fullName: 'gitea-gitea'}),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
main().then(exit).catch(exit);
|
|
||||||
|
|
|
@ -1,120 +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
|
|
||||||
|
|
||||||
//go:build ignore
|
|
||||||
// +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 profile '%s': %v", file, err)
|
|
||||||
}
|
|
||||||
for _, p := range profiles {
|
|
||||||
merged = addProfile(merged, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dumpProfiles(merged, os.Stdout)
|
|
||||||
}
|
|
|
@ -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/
|
|
720
cmd/admin.go
720
cmd/admin.go
|
@ -6,22 +6,10 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"text/tabwriter"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/git"
|
|
||||||
"code.gitea.io/gitea/modules/graceful"
|
|
||||||
"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"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/storage"
|
|
||||||
"code.gitea.io/gitea/services/auth/source/oauth2"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
@ -30,85 +18,48 @@ var (
|
||||||
// CmdAdmin represents the available admin sub-command.
|
// CmdAdmin represents the available admin sub-command.
|
||||||
CmdAdmin = cli.Command{
|
CmdAdmin = cli.Command{
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
Usage: "Command line interface to perform common administrative operations",
|
Usage: "Perform admin operations on command line",
|
||||||
|
Description: `Allow using internal logic of Gitea without hacking into the source code
|
||||||
|
to make automatic initialization process more smoothly`,
|
||||||
Subcommands: []cli.Command{
|
Subcommands: []cli.Command{
|
||||||
subcmdUser,
|
subcmdCreateUser,
|
||||||
subcmdRepoSyncReleases,
|
subcmdChangePassword,
|
||||||
subcmdRegenerate,
|
|
||||||
subcmdAuth,
|
|
||||||
subcmdSendMail,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
subcmdUser = cli.Command{
|
subcmdCreateUser = cli.Command{
|
||||||
Name: "user",
|
Name: "create-user",
|
||||||
Usage: "Modify users",
|
|
||||||
Subcommands: []cli.Command{
|
|
||||||
microcmdUserCreate,
|
|
||||||
microcmdUserList,
|
|
||||||
microcmdUserChangePassword,
|
|
||||||
microcmdUserDelete,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdUserList = cli.Command{
|
|
||||||
Name: "list",
|
|
||||||
Usage: "List users",
|
|
||||||
Action: runListUsers,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "admin",
|
|
||||||
Usage: "List only admin users",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdUserCreate = cli.Command{
|
|
||||||
Name: "create",
|
|
||||||
Usage: "Create a new user in database",
|
Usage: "Create a new user in database",
|
||||||
Action: runCreateUser,
|
Action: runCreateUser,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
Usage: "Username. DEPRECATED: use username instead",
|
Value: "",
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "username",
|
|
||||||
Usage: "Username",
|
Usage: "Username",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "password",
|
Name: "password",
|
||||||
|
Value: "",
|
||||||
Usage: "User password",
|
Usage: "User password",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "email",
|
Name: "email",
|
||||||
|
Value: "",
|
||||||
Usage: "User email address",
|
Usage: "User email address",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
Usage: "User is an admin",
|
Usage: "User is an admin",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.StringFlag{
|
||||||
Name: "random-password",
|
Name: "config, c",
|
||||||
Usage: "Generate a random password for the user",
|
Value: "custom/conf/app.ini",
|
||||||
},
|
Usage: "Custom configuration file path",
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "must-change-password",
|
|
||||||
Usage: "Set this option to false to prevent forcing the user to change their password after initial login, (Default: true)",
|
|
||||||
},
|
|
||||||
cli.IntFlag{
|
|
||||||
Name: "random-password-length",
|
|
||||||
Usage: "Length of the random password to be generated",
|
|
||||||
Value: 12,
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "access-token",
|
|
||||||
Usage: "Generate access token for the user",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
microcmdUserChangePassword = cli.Command{
|
subcmdChangePassword = cli.Command{
|
||||||
Name: "change-password",
|
Name: "change-password",
|
||||||
Usage: "Change a user's password",
|
Usage: "Change a user's password",
|
||||||
Action: runChangePassword,
|
Action: runChangePassword,
|
||||||
|
@ -125,629 +76,72 @@ var (
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
microcmdUserDelete = cli.Command{
|
|
||||||
Name: "delete",
|
|
||||||
Usage: "Delete specific user by id, name or email",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.Int64Flag{
|
|
||||||
Name: "id",
|
|
||||||
Usage: "ID of user of the user to delete",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "username,u",
|
|
||||||
Usage: "Username of the user to delete",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "email,e",
|
|
||||||
Usage: "Email of the user to delete",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Action: runDeleteUser,
|
|
||||||
}
|
|
||||||
|
|
||||||
subcmdRepoSyncReleases = cli.Command{
|
|
||||||
Name: "repo-sync-releases",
|
|
||||||
Usage: "Synchronize repository releases with tags",
|
|
||||||
Action: runRepoSyncReleases,
|
|
||||||
}
|
|
||||||
|
|
||||||
subcmdRegenerate = cli.Command{
|
|
||||||
Name: "regenerate",
|
|
||||||
Usage: "Regenerate specific files",
|
|
||||||
Subcommands: []cli.Command{
|
|
||||||
microcmdRegenHooks,
|
|
||||||
microcmdRegenKeys,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdRegenHooks = cli.Command{
|
|
||||||
Name: "hooks",
|
|
||||||
Usage: "Regenerate git-hooks",
|
|
||||||
Action: runRegenerateHooks,
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdRegenKeys = cli.Command{
|
|
||||||
Name: "keys",
|
|
||||||
Usage: "Regenerate authorized_keys file",
|
|
||||||
Action: runRegenerateKeys,
|
|
||||||
}
|
|
||||||
|
|
||||||
subcmdAuth = cli.Command{
|
|
||||||
Name: "auth",
|
|
||||||
Usage: "Modify external auth providers",
|
|
||||||
Subcommands: []cli.Command{
|
|
||||||
microcmdAuthAddOauth,
|
|
||||||
microcmdAuthUpdateOauth,
|
|
||||||
cmdAuthAddLdapBindDn,
|
|
||||||
cmdAuthUpdateLdapBindDn,
|
|
||||||
cmdAuthAddLdapSimpleAuth,
|
|
||||||
cmdAuthUpdateLdapSimpleAuth,
|
|
||||||
microcmdAuthList,
|
|
||||||
microcmdAuthDelete,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdAuthList = cli.Command{
|
|
||||||
Name: "list",
|
|
||||||
Usage: "List auth sources",
|
|
||||||
Action: runListAuth,
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
idFlag = cli.Int64Flag{
|
|
||||||
Name: "id",
|
|
||||||
Usage: "ID of authentication source",
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdAuthDelete = cli.Command{
|
|
||||||
Name: "delete",
|
|
||||||
Usage: "Delete specific auth source",
|
|
||||||
Flags: []cli.Flag{idFlag},
|
|
||||||
Action: runDeleteAuth,
|
|
||||||
}
|
|
||||||
|
|
||||||
oauthCLIFlags = []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Application Name",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "provider",
|
|
||||||
Value: "",
|
|
||||||
Usage: "OAuth2 Provider",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "key",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Client ID (Key)",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "secret",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Client Secret",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "auto-discover-url",
|
|
||||||
Value: "",
|
|
||||||
Usage: "OpenID Connect Auto Discovery URL (only required when using OpenID Connect as provider)",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "use-custom-urls",
|
|
||||||
Value: "false",
|
|
||||||
Usage: "Use custom URLs for GitLab/GitHub OAuth endpoints",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "custom-auth-url",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Use a custom Authorization URL (option for GitLab/GitHub)",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "custom-token-url",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Use a custom Token URL (option for GitLab/GitHub)",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "custom-profile-url",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Use a custom Profile URL (option for GitLab/GitHub)",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "custom-email-url",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Use a custom Email URL (option for GitHub)",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "icon-url",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Custom icon URL for OAuth2 login source",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdAuthUpdateOauth = cli.Command{
|
|
||||||
Name: "update-oauth",
|
|
||||||
Usage: "Update existing Oauth authentication source",
|
|
||||||
Action: runUpdateOauth,
|
|
||||||
Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...),
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdAuthAddOauth = cli.Command{
|
|
||||||
Name: "add-oauth",
|
|
||||||
Usage: "Add new Oauth authentication source",
|
|
||||||
Action: runAddOauth,
|
|
||||||
Flags: oauthCLIFlags,
|
|
||||||
}
|
|
||||||
|
|
||||||
subcmdSendMail = cli.Command{
|
|
||||||
Name: "sendmail",
|
|
||||||
Usage: "Send a message to all users",
|
|
||||||
Action: runSendMail,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "title",
|
|
||||||
Usage: `a title of a message`,
|
|
||||||
Value: "",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "content",
|
|
||||||
Usage: "a content of a message",
|
|
||||||
Value: "",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "force,f",
|
|
||||||
Usage: "A flag to bypass a confirmation step",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func runChangePassword(c *cli.Context) error {
|
func runChangePassword(c *cli.Context) error {
|
||||||
if err := argsSet(c, "username", "password"); err != nil {
|
if !c.IsSet("password") {
|
||||||
return err
|
return fmt.Errorf("Password is not specified")
|
||||||
|
} else if !c.IsSet("username") {
|
||||||
|
return fmt.Errorf("Username is not specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := initDB(); err != nil {
|
setting.NewContext()
|
||||||
return err
|
models.LoadConfigs()
|
||||||
}
|
|
||||||
if !pwd.IsComplexEnough(c.String("password")) {
|
setting.NewXORMLogService(false)
|
||||||
return errors.New("Password does not meet complexity requirements")
|
if err := models.SetEngine(); err != nil {
|
||||||
}
|
return fmt.Errorf("models.SetEngine: %v", err)
|
||||||
pwned, err := pwd.IsPwned(context.Background(), c.String("password"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if pwned {
|
|
||||||
return errors.New("The password you chose is on a list of stolen passwords previously exposed in public data breaches. Please try again with a different password.\nFor more details, see https://haveibeenpwned.com/Passwords")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uname := c.String("username")
|
uname := c.String("username")
|
||||||
user, err := models.GetUserByName(uname)
|
user, err := models.GetUserByName(uname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("%v", err)
|
||||||
}
|
}
|
||||||
if err = user.SetPassword(c.String("password")); err != nil {
|
user.Passwd = c.String("password")
|
||||||
return err
|
if user.Salt, err = models.GetUserSalt(); err != nil {
|
||||||
|
return fmt.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
user.EncodePasswd()
|
||||||
|
if err := models.UpdateUserCols(user, "passwd", "salt"); err != nil {
|
||||||
|
return fmt.Errorf("%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = models.UpdateUserCols(user, "passwd", "passwd_hash_algo", "salt"); err != nil {
|
fmt.Printf("User '%s' password has been successfully updated!\n", uname)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("%s's password has been successfully updated!\n", user.Name)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCreateUser(c *cli.Context) error {
|
func runCreateUser(c *cli.Context) error {
|
||||||
if err := argsSet(c, "email"); err != nil {
|
if !c.IsSet("name") {
|
||||||
return err
|
return fmt.Errorf("Username is not specified")
|
||||||
|
} else if !c.IsSet("password") {
|
||||||
|
return fmt.Errorf("Password is not specified")
|
||||||
|
} else if !c.IsSet("email") {
|
||||||
|
return fmt.Errorf("Email is not specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.IsSet("name") && c.IsSet("username") {
|
if c.IsSet("config") {
|
||||||
return errors.New("Cannot set both --name and --username flags")
|
setting.CustomConf = c.String("config")
|
||||||
}
|
|
||||||
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") {
|
setting.NewContext()
|
||||||
return errors.New("cannot set both -random-password and -password flags")
|
models.LoadConfigs()
|
||||||
|
|
||||||
|
setting.NewXORMLogService(false)
|
||||||
|
if err := models.SetEngine(); err != nil {
|
||||||
|
return fmt.Errorf("models.SetEngine: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var username string
|
if err := models.CreateUser(&models.User{
|
||||||
if c.IsSet("username") {
|
Name: c.String("name"),
|
||||||
username = c.String("username")
|
Email: c.String("email"),
|
||||||
} else {
|
Passwd: c.String("password"),
|
||||||
username = c.String("name")
|
IsActive: true,
|
||||||
fmt.Fprintf(os.Stderr, "--name flag is deprecated. Use --username instead.\n")
|
IsAdmin: c.Bool("admin"),
|
||||||
}
|
}); err != nil {
|
||||||
|
|
||||||
if err := initDB(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var password string
|
|
||||||
if c.IsSet("password") {
|
|
||||||
password = c.String("password")
|
|
||||||
} else if c.IsSet("random-password") {
|
|
||||||
var err error
|
|
||||||
password, err = pwd.Generate(c.Int("random-password-length"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Printf("generated random password is '%s'\n", password)
|
|
||||||
} else {
|
|
||||||
return errors.New("must set either password or random-password flag")
|
|
||||||
}
|
|
||||||
|
|
||||||
// always default to true
|
|
||||||
var changePassword = true
|
|
||||||
|
|
||||||
// If this is the first user being created.
|
|
||||||
// Take it as the admin and don't force a password update.
|
|
||||||
if n := models.CountUsers(); n == 0 {
|
|
||||||
changePassword = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("must-change-password") {
|
|
||||||
changePassword = c.Bool("must-change-password")
|
|
||||||
}
|
|
||||||
|
|
||||||
u := &models.User{
|
|
||||||
Name: username,
|
|
||||||
Email: c.String("email"),
|
|
||||||
Passwd: password,
|
|
||||||
IsActive: true,
|
|
||||||
IsAdmin: c.Bool("admin"),
|
|
||||||
MustChangePassword: changePassword,
|
|
||||||
Theme: setting.UI.DefaultTheme,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := models.CreateUser(u); err != nil {
|
|
||||||
return fmt.Errorf("CreateUser: %v", err)
|
return fmt.Errorf("CreateUser: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Bool("access-token") {
|
fmt.Printf("New user '%s' has been successfully created!\n", c.String("name"))
|
||||||
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)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runListUsers(c *cli.Context) error {
|
|
||||||
if err := initDB(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
users, err := models.GetAllUsers()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0)
|
|
||||||
|
|
||||||
if c.IsSet("admin") {
|
|
||||||
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\n")
|
|
||||||
for _, u := range users {
|
|
||||||
if u.IsAdmin {
|
|
||||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", u.ID, u.Name, u.Email, u.IsActive)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\n")
|
|
||||||
for _, u := range users {
|
|
||||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Flush()
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func runDeleteUser(c *cli.Context) error {
|
|
||||||
if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
|
|
||||||
return fmt.Errorf("You must provide the id, username or email of a user to delete")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := initDB(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := storage.Init(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var user *models.User
|
|
||||||
if c.IsSet("email") {
|
|
||||||
user, err = models.GetUserByEmail(c.String("email"))
|
|
||||||
} else if c.IsSet("username") {
|
|
||||||
user, err = models.GetUserByName(c.String("username"))
|
|
||||||
} else {
|
|
||||||
user, err = models.GetUserByID(c.Int64("id"))
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) {
|
|
||||||
return fmt.Errorf("The user %s who has email %s does not match the provided username %s", user.Name, c.String("email"), c.String("username"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("id") && user.ID != c.Int64("id") {
|
|
||||||
return fmt.Errorf("The user %s does not match the provided id %d", user.Name, c.Int64("id"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return models.DeleteUser(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
func runRepoSyncReleases(_ *cli.Context) error {
|
|
||||||
if err := initDB(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Trace("Synchronizing repository releases (this may take a while)")
|
|
||||||
for page := 1; ; page++ {
|
|
||||||
repos, count, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
|
|
||||||
ListOptions: models.ListOptions{
|
|
||||||
PageSize: models.RepositoryListDefaultPageSize,
|
|
||||||
Page: page,
|
|
||||||
},
|
|
||||||
Private: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("SearchRepositoryByName: %v", err)
|
|
||||||
}
|
|
||||||
if len(repos) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
log.Trace("Processing next %d repos of %d", len(repos), count)
|
|
||||||
for _, repo := range repos {
|
|
||||||
log.Trace("Synchronizing repo %s with path %s", repo.FullName(), repo.RepoPath())
|
|
||||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("OpenRepository: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
oldnum, err := getReleaseCount(repo.ID)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn(" GetReleaseCountByRepoID: %v", err)
|
|
||||||
}
|
|
||||||
log.Trace(" currentNumReleases is %d, running SyncReleasesWithTags", oldnum)
|
|
||||||
|
|
||||||
if err = repo_module.SyncReleasesWithTags(repo, gitRepo); err != nil {
|
|
||||||
log.Warn(" SyncReleasesWithTags: %v", err)
|
|
||||||
gitRepo.Close()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
count, err = getReleaseCount(repo.ID)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn(" GetReleaseCountByRepoID: %v", err)
|
|
||||||
gitRepo.Close()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Trace(" repo %s releases synchronized to tags: from %d to %d",
|
|
||||||
repo.FullName(), oldnum, count)
|
|
||||||
gitRepo.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getReleaseCount(id int64) (int64, error) {
|
|
||||||
return models.GetReleaseCountByRepoID(
|
|
||||||
id,
|
|
||||||
models.FindReleasesOptions{
|
|
||||||
IncludeTags: true,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func runRegenerateHooks(_ *cli.Context) error {
|
|
||||||
if err := initDB(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return repo_module.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
|
|
||||||
}
|
|
||||||
|
|
||||||
func runRegenerateKeys(_ *cli.Context) error {
|
|
||||||
if err := initDB(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return models.RewriteAllPublicKeys()
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseOAuth2Config(c *cli.Context) *oauth2.Source {
|
|
||||||
var customURLMapping *oauth2.CustomURLMapping
|
|
||||||
if c.IsSet("use-custom-urls") {
|
|
||||||
customURLMapping = &oauth2.CustomURLMapping{
|
|
||||||
TokenURL: c.String("custom-token-url"),
|
|
||||||
AuthURL: c.String("custom-auth-url"),
|
|
||||||
ProfileURL: c.String("custom-profile-url"),
|
|
||||||
EmailURL: c.String("custom-email-url"),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
customURLMapping = nil
|
|
||||||
}
|
|
||||||
return &oauth2.Source{
|
|
||||||
Provider: c.String("provider"),
|
|
||||||
ClientID: c.String("key"),
|
|
||||||
ClientSecret: c.String("secret"),
|
|
||||||
OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
|
|
||||||
CustomURLMapping: customURLMapping,
|
|
||||||
IconURL: c.String("icon-url"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runAddOauth(c *cli.Context) error {
|
|
||||||
if err := initDB(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return models.CreateLoginSource(&models.LoginSource{
|
|
||||||
Type: models.LoginOAuth2,
|
|
||||||
Name: c.String("name"),
|
|
||||||
IsActive: true,
|
|
||||||
Cfg: parseOAuth2Config(c),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func runUpdateOauth(c *cli.Context) error {
|
|
||||||
if !c.IsSet("id") {
|
|
||||||
return fmt.Errorf("--id flag is missing")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := initDB(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
source, err := models.GetLoginSourceByID(c.Int64("id"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
oAuth2Config := source.Cfg.(*oauth2.Source)
|
|
||||||
|
|
||||||
if c.IsSet("name") {
|
|
||||||
source.Name = c.String("name")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("provider") {
|
|
||||||
oAuth2Config.Provider = c.String("provider")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("key") {
|
|
||||||
oAuth2Config.ClientID = c.String("key")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("secret") {
|
|
||||||
oAuth2Config.ClientSecret = c.String("secret")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("auto-discover-url") {
|
|
||||||
oAuth2Config.OpenIDConnectAutoDiscoveryURL = c.String("auto-discover-url")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("icon-url") {
|
|
||||||
oAuth2Config.IconURL = c.String("icon-url")
|
|
||||||
}
|
|
||||||
|
|
||||||
// update custom URL mapping
|
|
||||||
var customURLMapping = &oauth2.CustomURLMapping{}
|
|
||||||
|
|
||||||
if oAuth2Config.CustomURLMapping != nil {
|
|
||||||
customURLMapping.TokenURL = oAuth2Config.CustomURLMapping.TokenURL
|
|
||||||
customURLMapping.AuthURL = oAuth2Config.CustomURLMapping.AuthURL
|
|
||||||
customURLMapping.ProfileURL = oAuth2Config.CustomURLMapping.ProfileURL
|
|
||||||
customURLMapping.EmailURL = oAuth2Config.CustomURLMapping.EmailURL
|
|
||||||
}
|
|
||||||
if c.IsSet("use-custom-urls") && c.IsSet("custom-token-url") {
|
|
||||||
customURLMapping.TokenURL = c.String("custom-token-url")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("use-custom-urls") && c.IsSet("custom-auth-url") {
|
|
||||||
customURLMapping.AuthURL = c.String("custom-auth-url")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("use-custom-urls") && c.IsSet("custom-profile-url") {
|
|
||||||
customURLMapping.ProfileURL = c.String("custom-profile-url")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("use-custom-urls") && c.IsSet("custom-email-url") {
|
|
||||||
customURLMapping.EmailURL = c.String("custom-email-url")
|
|
||||||
}
|
|
||||||
|
|
||||||
oAuth2Config.CustomURLMapping = customURLMapping
|
|
||||||
source.Cfg = oAuth2Config
|
|
||||||
|
|
||||||
return models.UpdateSource(source)
|
|
||||||
}
|
|
||||||
|
|
||||||
func runListAuth(c *cli.Context) error {
|
|
||||||
if err := initDB(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
loginSources, err := models.LoginSources()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
flags := tabwriter.AlignRight
|
|
||||||
if c.Bool("vertical-bars") {
|
|
||||||
flags |= tabwriter.Debug
|
|
||||||
}
|
|
||||||
|
|
||||||
padChar := byte('\t')
|
|
||||||
if len(c.String("pad-char")) > 0 {
|
|
||||||
padChar = c.String("pad-char")[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// loop through each source and print
|
|
||||||
w := tabwriter.NewWriter(os.Stdout, c.Int("min-width"), c.Int("tab-width"), c.Int("padding"), padChar, flags)
|
|
||||||
fmt.Fprintf(w, "ID\tName\tType\tEnabled\n")
|
|
||||||
for _, source := range loginSources {
|
|
||||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, models.LoginNames[source.Type], source.IsActive)
|
|
||||||
}
|
|
||||||
w.Flush()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func runDeleteAuth(c *cli.Context) error {
|
|
||||||
if !c.IsSet("id") {
|
|
||||||
return fmt.Errorf("--id flag is missing")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := initDB(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
source, err := models.GetLoginSourceByID(c.Int64("id"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return models.DeleteSource(source)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,369 +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/services/auth/source/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.IsActive = !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 *ldap.Source) error {
|
|
||||||
if c.IsSet("name") {
|
|
||||||
config.Name = c.String("name")
|
|
||||||
}
|
|
||||||
if c.IsSet("host") {
|
|
||||||
config.Host = c.String("host")
|
|
||||||
}
|
|
||||||
if c.IsSet("port") {
|
|
||||||
config.Port = c.Int("port")
|
|
||||||
}
|
|
||||||
if c.IsSet("security-protocol") {
|
|
||||||
p, ok := findLdapSecurityProtocolByName(c.String("security-protocol"))
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("Unknown security protocol name: %s", c.String("security-protocol"))
|
|
||||||
}
|
|
||||||
config.SecurityProtocol = p
|
|
||||||
}
|
|
||||||
if c.IsSet("skip-tls-verify") {
|
|
||||||
config.SkipVerify = c.Bool("skip-tls-verify")
|
|
||||||
}
|
|
||||||
if c.IsSet("bind-dn") {
|
|
||||||
config.BindDN = c.String("bind-dn")
|
|
||||||
}
|
|
||||||
if c.IsSet("user-dn") {
|
|
||||||
config.UserDN = c.String("user-dn")
|
|
||||||
}
|
|
||||||
if c.IsSet("bind-password") {
|
|
||||||
config.BindPassword = c.String("bind-password")
|
|
||||||
}
|
|
||||||
if c.IsSet("user-search-base") {
|
|
||||||
config.UserBase = c.String("user-search-base")
|
|
||||||
}
|
|
||||||
if c.IsSet("username-attribute") {
|
|
||||||
config.AttributeUsername = c.String("username-attribute")
|
|
||||||
}
|
|
||||||
if c.IsSet("firstname-attribute") {
|
|
||||||
config.AttributeName = c.String("firstname-attribute")
|
|
||||||
}
|
|
||||||
if c.IsSet("surname-attribute") {
|
|
||||||
config.AttributeSurname = c.String("surname-attribute")
|
|
||||||
}
|
|
||||||
if c.IsSet("email-attribute") {
|
|
||||||
config.AttributeMail = c.String("email-attribute")
|
|
||||||
}
|
|
||||||
if c.IsSet("attributes-in-bind") {
|
|
||||||
config.AttributesInBind = c.Bool("attributes-in-bind")
|
|
||||||
}
|
|
||||||
if c.IsSet("public-ssh-key-attribute") {
|
|
||||||
config.AttributeSSHPublicKey = c.String("public-ssh-key-attribute")
|
|
||||||
}
|
|
||||||
if c.IsSet("page-size") {
|
|
||||||
config.SearchPageSize = uint32(c.Uint("page-size"))
|
|
||||||
}
|
|
||||||
if c.IsSet("user-filter") {
|
|
||||||
config.Filter = c.String("user-filter")
|
|
||||||
}
|
|
||||||
if c.IsSet("admin-filter") {
|
|
||||||
config.AdminFilter = c.String("admin-filter")
|
|
||||||
}
|
|
||||||
if c.IsSet("restricted-filter") {
|
|
||||||
config.RestrictedFilter = c.String("restricted-filter")
|
|
||||||
}
|
|
||||||
if c.IsSet("allow-deactivate-all") {
|
|
||||||
config.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 ldap.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,
|
|
||||||
IsActive: true, // active by default
|
|
||||||
Cfg: &ldap.Source{
|
|
||||||
Enabled: true, // always true
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
parseLoginSource(c, loginSource)
|
|
||||||
if err := parseLdapConfig(c, loginSource.Cfg.(*ldap.Source)); 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.Cfg.(*ldap.Source)); 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,
|
|
||||||
IsActive: true, // active by default
|
|
||||||
Cfg: &ldap.Source{
|
|
||||||
Enabled: true, // always true
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
parseLoginSource(c, loginSource)
|
|
||||||
if err := parseLdapConfig(c, loginSource.Cfg.(*ldap.Source)); 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.Cfg.(*ldap.Source)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.updateLoginSource(loginSource)
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
47
cmd/cert.go
47
cmd/cert.go
|
@ -90,16 +90,16 @@ func pemBlockForKey(priv interface{}) *pem.Block {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCert(c *cli.Context) error {
|
func runCert(ctx *cli.Context) error {
|
||||||
if err := argsSet(c, "host"); err != nil {
|
if len(ctx.String("host")) == 0 {
|
||||||
return err
|
log.Fatal("Missing required --host parameter")
|
||||||
}
|
}
|
||||||
|
|
||||||
var priv interface{}
|
var priv interface{}
|
||||||
var err error
|
var err error
|
||||||
switch c.String("ecdsa-curve") {
|
switch ctx.String("ecdsa-curve") {
|
||||||
case "":
|
case "":
|
||||||
priv, err = rsa.GenerateKey(rand.Reader, c.Int("rsa-bits"))
|
priv, err = rsa.GenerateKey(rand.Reader, ctx.Int("rsa-bits"))
|
||||||
case "P224":
|
case "P224":
|
||||||
priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
|
priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
|
||||||
case "P256":
|
case "P256":
|
||||||
|
@ -109,23 +109,23 @@ func runCert(c *cli.Context) error {
|
||||||
case "P521":
|
case "P521":
|
||||||
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||||
default:
|
default:
|
||||||
log.Fatalf("Unrecognized elliptic curve: %q", c.String("ecdsa-curve"))
|
log.Fatalf("Unrecognized elliptic curve: %q", ctx.String("ecdsa-curve"))
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to generate private key: %v", err)
|
log.Fatalf("Failed to generate private key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var notBefore time.Time
|
var notBefore time.Time
|
||||||
if startDate := c.String("start-date"); startDate != "" {
|
if len(ctx.String("start-date")) == 0 {
|
||||||
notBefore, err = time.Parse("Jan 2 15:04:05 2006", startDate)
|
notBefore = time.Now()
|
||||||
|
} else {
|
||||||
|
notBefore, err = time.Parse("Jan 2 15:04:05 2006", ctx.String("start-date"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to parse creation date: %v", err)
|
log.Fatalf("Failed to parse creation date: %v", err)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
notBefore = time.Now()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
notAfter := notBefore.Add(c.Duration("duration"))
|
notAfter := notBefore.Add(ctx.Duration("duration"))
|
||||||
|
|
||||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||||
|
@ -147,7 +147,7 @@ func runCert(c *cli.Context) error {
|
||||||
BasicConstraintsValid: true,
|
BasicConstraintsValid: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
hosts := strings.Split(c.String("host"), ",")
|
hosts := strings.Split(ctx.String("host"), ",")
|
||||||
for _, h := range hosts {
|
for _, h := range hosts {
|
||||||
if ip := net.ParseIP(h); ip != nil {
|
if ip := net.ParseIP(h); ip != nil {
|
||||||
template.IPAddresses = append(template.IPAddresses, ip)
|
template.IPAddresses = append(template.IPAddresses, ip)
|
||||||
|
@ -156,7 +156,7 @@ func runCert(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Bool("ca") {
|
if ctx.Bool("ca") {
|
||||||
template.IsCA = true
|
template.IsCA = true
|
||||||
template.KeyUsage |= x509.KeyUsageCertSign
|
template.KeyUsage |= x509.KeyUsageCertSign
|
||||||
}
|
}
|
||||||
|
@ -170,28 +170,17 @@ func runCert(c *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to open cert.pem for writing: %v", err)
|
log.Fatalf("Failed to open cert.pem for writing: %v", err)
|
||||||
}
|
}
|
||||||
err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||||
if err != nil {
|
certOut.Close()
|
||||||
log.Fatalf("Failed to encode certificate: %v", err)
|
|
||||||
}
|
|
||||||
err = certOut.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to write cert: %v", err)
|
|
||||||
}
|
|
||||||
log.Println("Written cert.pem")
|
log.Println("Written cert.pem")
|
||||||
|
|
||||||
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to open key.pem for writing: %v", err)
|
log.Fatalf("Failed to open key.pem for writing: %v", err)
|
||||||
}
|
}
|
||||||
err = pem.Encode(keyOut, pemBlockForKey(priv))
|
pem.Encode(keyOut, pemBlockForKey(priv))
|
||||||
if err != nil {
|
keyOut.Close()
|
||||||
log.Fatalf("Failed to encode key: %v", err)
|
|
||||||
}
|
|
||||||
err = keyOut.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to write key: %v", err)
|
|
||||||
}
|
|
||||||
log.Println("Written key.pem")
|
log.Println("Written key.pem")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
94
cmd/cmd.go
94
cmd/cmd.go
|
@ -1,94 +0,0 @@
|
||||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package cmd provides subcommands to the gitea binary - such as "web" or
|
|
||||||
// "admin".
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
// argsSet checks that all the required arguments are set. args is a list of
|
|
||||||
// arguments that must be set in the passed Context.
|
|
||||||
func argsSet(c *cli.Context, args ...string) error {
|
|
||||||
for _, a := range args {
|
|
||||||
if !c.IsSet(a) {
|
|
||||||
return errors.New(a + " is not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
if util.IsEmptyString(a) {
|
|
||||||
return errors.New(a + " is required")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// confirm waits for user input which confirms an action
|
|
||||||
func confirm() (bool, error) {
|
|
||||||
var response string
|
|
||||||
|
|
||||||
_, err := fmt.Scanln(&response)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch strings.ToLower(response) {
|
|
||||||
case "y", "yes":
|
|
||||||
return true, nil
|
|
||||||
case "n", "no":
|
|
||||||
return false, nil
|
|
||||||
default:
|
|
||||||
return false, errors.New(response + " isn't a correct confirmation string")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func initDB() error {
|
|
||||||
return initDBDisableConsole(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func initDBDisableConsole(disableConsole bool) error {
|
|
||||||
setting.NewContext()
|
|
||||||
setting.InitDBConfig()
|
|
||||||
|
|
||||||
setting.NewXORMLogService(disableConsole)
|
|
||||||
if err := models.SetEngine(); err != nil {
|
|
||||||
return fmt.Errorf("models.SetEngine: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func installSignals() (context.Context, context.CancelFunc) {
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
go func() {
|
|
||||||
// install notify
|
|
||||||
signalChannel := make(chan os.Signal, 1)
|
|
||||||
|
|
||||||
signal.Notify(
|
|
||||||
signalChannel,
|
|
||||||
syscall.SIGINT,
|
|
||||||
syscall.SIGTERM,
|
|
||||||
)
|
|
||||||
select {
|
|
||||||
case <-signalChannel:
|
|
||||||
case <-ctx.Done():
|
|
||||||
}
|
|
||||||
cancel()
|
|
||||||
signal.Reset()
|
|
||||||
}()
|
|
||||||
|
|
||||||
return ctx, cancel
|
|
||||||
}
|
|
|
@ -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.Info("AppPath: %s", setting.AppPath)
|
|
||||||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
|
||||||
log.Info("Custom path: %s", setting.CustomPath)
|
|
||||||
log.Info("Log path: %s", setting.LogRootPath)
|
|
||||||
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
|
|
||||||
}
|
|
61
cmd/docs.go
61
cmd/docs.go
|
@ -1,61 +0,0 @@
|
||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CmdDocs represents the available docs sub-command.
|
|
||||||
var CmdDocs = cli.Command{
|
|
||||||
Name: "docs",
|
|
||||||
Usage: "Output CLI documentation",
|
|
||||||
Description: "A command to output Gitea's CLI documentation, optionally to a file.",
|
|
||||||
Action: runDocs,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "man",
|
|
||||||
Usage: "Output man pages instead",
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "output, o",
|
|
||||||
Usage: "Path to output to instead of stdout (will overwrite if exists)",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func runDocs(ctx *cli.Context) error {
|
|
||||||
docs, err := ctx.App.ToMarkdown()
|
|
||||||
if ctx.Bool("man") {
|
|
||||||
docs, err = ctx.App.ToMan()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ctx.Bool("man") {
|
|
||||||
// Clean up markdown. The following bug was fixed in v2, but is present in v1.
|
|
||||||
// It affects markdown output (even though the issue is referring to man pages)
|
|
||||||
// https://github.com/urfave/cli/issues/1040
|
|
||||||
docs = docs[strings.Index(docs, "#"):]
|
|
||||||
}
|
|
||||||
|
|
||||||
out := os.Stdout
|
|
||||||
if ctx.String("output") != "" {
|
|
||||||
fi, err := os.Create(ctx.String("output"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer fi.Close()
|
|
||||||
out = fi
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = fmt.Fprintln(out, docs)
|
|
||||||
return err
|
|
||||||
}
|
|
215
cmd/doctor.go
215
cmd/doctor.go
|
@ -1,215 +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 (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
golog "log"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"text/tabwriter"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/migrations"
|
|
||||||
"code.gitea.io/gitea/modules/doctor"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
|
|
||||||
"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`,
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "color, H",
|
|
||||||
Usage: "Use color for outputted information",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
|
|
||||||
colorize := log.CanColorStdout
|
|
||||||
if ctx.IsSet("color") {
|
|
||||||
colorize = ctx.Bool("color")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(logFile) == 0 {
|
|
||||||
log.NewLogger(1000, "doctor", "console", fmt.Sprintf(`{"level":"NONE","stacktracelevel":"NONE","colorize":%t}`, colorize))
|
|
||||||
} else if logFile == "-" {
|
|
||||||
log.NewLogger(1000, "doctor", "console", fmt.Sprintf(`{"level":"trace","stacktracelevel":"NONE","colorize":%t}`, colorize))
|
|
||||||
} else {
|
|
||||||
log.NewLogger(1000, "doctor", "file", fmt.Sprintf(`{"filename":%q,"level":"trace","stacktracelevel":"NONE"}`, logFile))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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, 1, '\t', 0)
|
|
||||||
_, _ = w.Write([]byte("Default\tName\tTitle\n"))
|
|
||||||
for _, check := range doctor.Checks {
|
|
||||||
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 []*doctor.Check
|
|
||||||
if ctx.Bool("all") {
|
|
||||||
checks = doctor.Checks
|
|
||||||
} 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 doctor.Checks {
|
|
||||||
if addDefault && check.IsDefault {
|
|
||||||
checks = append(checks, check)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, name := range names {
|
|
||||||
if name == check.Name {
|
|
||||||
checks = append(checks, check)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for _, check := range doctor.Checks {
|
|
||||||
if check.IsDefault {
|
|
||||||
checks = append(checks, check)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we can set up our own logger to return information about what the doctor is doing
|
|
||||||
if err := log.NewNamedLogger("doctorouter",
|
|
||||||
1000,
|
|
||||||
"console",
|
|
||||||
"console",
|
|
||||||
fmt.Sprintf(`{"level":"INFO","stacktracelevel":"NONE","colorize":%t,"flags":-1}`, colorize)); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger := log.GetLogger("doctorouter")
|
|
||||||
defer logger.Close()
|
|
||||||
return doctor.RunChecks(logger, ctx.Bool("fix"), checks)
|
|
||||||
}
|
|
397
cmd/dump.go
397
cmd/dump.go
|
@ -8,89 +8,20 @@ package cmd
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/json"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/storage"
|
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
|
|
||||||
"gitea.com/go-chi/session"
|
"github.com/Unknwon/cae/zip"
|
||||||
archiver "github.com/mholt/archiver/v3"
|
"github.com/Unknwon/com"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
func addFile(w archiver.Writer, filePath 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 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.
|
// CmdDump represents the available dump sub-command.
|
||||||
var CmdDump = cli.Command{
|
var CmdDump = cli.Command{
|
||||||
Name: "dump",
|
Name: "dump",
|
||||||
|
@ -100,12 +31,12 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
|
||||||
Action: runDump,
|
Action: runDump,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "file, f",
|
Name: "config, c",
|
||||||
Value: fmt.Sprintf("gitea-dump-%d.zip", time.Now().Unix()),
|
Value: "custom/conf/app.ini",
|
||||||
Usage: "Name of the dump file which will be created. Supply '-' for stdout. See type for available types.",
|
Usage: "Custom configuration file path",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "verbose, V",
|
Name: "verbose, v",
|
||||||
Usage: "Show process details",
|
Usage: "Show process details",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
|
@ -117,278 +48,116 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
|
||||||
Name: "database, d",
|
Name: "database, d",
|
||||||
Usage: "Specify the database SQL syntax",
|
Usage: "Specify the database SQL syntax",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "skip-repository, R",
|
|
||||||
Usage: "Skip the repository dumping",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "skip-log, L",
|
|
||||||
Usage: "Skip the log dumping",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "skip-custom-dir",
|
|
||||||
Usage: "Skip custom directory",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "skip-lfs-data",
|
|
||||||
Usage: "Skip LFS data",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "skip-attachment-data",
|
|
||||||
Usage: "Skip attachment data",
|
|
||||||
},
|
|
||||||
cli.GenericFlag{
|
|
||||||
Name: "type",
|
|
||||||
Value: outputTypeEnum,
|
|
||||||
Usage: fmt.Sprintf("Dump output format: %s", outputTypeEnum.Join()),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func fatal(format string, args ...interface{}) {
|
|
||||||
fmt.Fprintf(os.Stderr, format+"\n", args...)
|
|
||||||
log.Fatal(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func runDump(ctx *cli.Context) error {
|
func runDump(ctx *cli.Context) error {
|
||||||
var file *os.File
|
if ctx.IsSet("config") {
|
||||||
fileName := ctx.String("file")
|
setting.CustomConf = ctx.String("config")
|
||||||
if fileName == "-" {
|
|
||||||
file = os.Stdout
|
|
||||||
err := log.DelLogger("console")
|
|
||||||
if err != nil {
|
|
||||||
fatal("Deleting default logger failed. Can not write to stdout: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
setting.NewContext()
|
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
|
setting.NewServices() // cannot access session settings otherwise
|
||||||
|
models.LoadConfigs()
|
||||||
|
|
||||||
err := models.SetEngine()
|
err := models.SetEngine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := storage.Init(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if file == nil {
|
|
||||||
file, err = os.Create(fileName)
|
|
||||||
if err != nil {
|
|
||||||
fatal("Unable to open %s: %v", fileName, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
absFileName, err := filepath.Abs(fileName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
verbose := ctx.Bool("verbose")
|
|
||||||
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 := addRecursiveExclude(w, "repos", setting.RepoRootPath, []string{absFileName}, verbose); err != nil {
|
|
||||||
fatal("Failed to include repositories: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.IsSet("skip-lfs-data") && ctx.Bool("skip-lfs-data") {
|
|
||||||
log.Info("Skip dumping LFS data")
|
|
||||||
} else if err := storage.LFS.IterateObjects(func(objPath string, object storage.Object) error {
|
|
||||||
info, err := object.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return w.Write(archiver.File{
|
|
||||||
FileInfo: archiver.FileInfo{
|
|
||||||
FileInfo: info,
|
|
||||||
CustomName: path.Join("data", "lfs", objPath),
|
|
||||||
},
|
|
||||||
ReadCloser: object,
|
|
||||||
})
|
|
||||||
}); err != nil {
|
|
||||||
fatal("Failed to dump LFS objects: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpDir := ctx.String("tempdir")
|
tmpDir := ctx.String("tempdir")
|
||||||
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
|
||||||
fatal("Path does not exist: %s", tmpDir)
|
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")
|
reposDump := path.Join(TmpWorkDir, "gitea-repo.zip")
|
||||||
if err != nil {
|
dbDump := path.Join(TmpWorkDir, "gitea-db.sql")
|
||||||
fatal("Failed to create tmp file: %v", err)
|
|
||||||
|
log.Printf("Dumping local repositories...%s", setting.RepoRootPath)
|
||||||
|
zip.Verbose = ctx.Bool("verbose")
|
||||||
|
if err := zip.PackTo(setting.RepoRootPath, reposDump, true); err != nil {
|
||||||
|
log.Fatalf("Failed to dump local repositories: %v", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
|
||||||
if err := util.Remove(dbDump.Name()); err != nil {
|
|
||||||
log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
targetDBType := ctx.String("database")
|
targetDBType := ctx.String("database")
|
||||||
if len(targetDBType) > 0 && targetDBType != setting.Database.Type {
|
if len(targetDBType) > 0 && targetDBType != models.DbCfg.Type {
|
||||||
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
|
log.Printf("Dumping database %s => %s...", models.DbCfg.Type, targetDBType)
|
||||||
} else {
|
} else {
|
||||||
log.Info("Dumping database...")
|
log.Printf("Dumping database...")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := models.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
|
if err := models.DumpDatabase(dbDump, targetDBType); err != nil {
|
||||||
fatal("Failed to dump database: %v", err)
|
log.Fatalf("Failed to dump database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := addFile(w, "gitea-db.sql", dbDump.Name(), verbose); err != nil {
|
fileName := fmt.Sprintf("gitea-dump-%d.zip", time.Now().Unix())
|
||||||
fatal("Failed to include gitea-db.sql: %v", err)
|
log.Printf("Packing dump files...")
|
||||||
}
|
z, err := zip.Create(fileName)
|
||||||
|
|
||||||
if len(setting.CustomConf) > 0 {
|
|
||||||
log.Info("Adding custom configuration file from %s", setting.CustomConf)
|
|
||||||
if err := addFile(w, "app.ini", setting.CustomConf, verbose); err != nil {
|
|
||||||
fatal("Failed to include specified app.ini: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.IsSet("skip-custom-dir") && ctx.Bool("skip-custom-dir") {
|
|
||||||
log.Info("Skipping custom directory")
|
|
||||||
} else {
|
|
||||||
customDir, err := os.Stat(setting.CustomPath)
|
|
||||||
if err == nil && customDir.IsDir() {
|
|
||||||
if is, _ := isSubdir(setting.AppDataPath, setting.CustomPath); !is {
|
|
||||||
if err := addRecursiveExclude(w, "custom", setting.CustomPath, []string{absFileName}, verbose); err != nil {
|
|
||||||
fatal("Failed to include custom: %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Info("Custom dir %s is inside data dir %s, skipped", setting.CustomPath, setting.AppDataPath)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Info("Custom dir %s doesn't exist, skipped", setting.CustomPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isExist, err := util.IsExist(setting.AppDataPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Unable to check if %s exists. Error: %v", setting.AppDataPath, err)
|
log.Fatalf("Failed to create %s: %v", fileName, err)
|
||||||
}
|
|
||||||
if isExist {
|
|
||||||
log.Info("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)
|
|
||||||
}
|
|
||||||
|
|
||||||
excludes = append(excludes, setting.RepoRootPath)
|
|
||||||
excludes = append(excludes, setting.LFS.Path)
|
|
||||||
excludes = append(excludes, setting.Attachment.Path)
|
|
||||||
excludes = append(excludes, setting.LogRootPath)
|
|
||||||
excludes = append(excludes, absFileName)
|
|
||||||
if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil {
|
|
||||||
fatal("Failed to include data directory: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.IsSet("skip-attachment-data") && ctx.Bool("skip-attachment-data") {
|
if err := z.AddFile("gitea-repo.zip", reposDump); err != nil {
|
||||||
log.Info("Skip dumping attachment data")
|
log.Fatalf("Failed to include gitea-repo.zip: %v", err)
|
||||||
} else if err := storage.Attachments.IterateObjects(func(objPath string, object storage.Object) error {
|
|
||||||
info, err := object.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return w.Write(archiver.File{
|
|
||||||
FileInfo: archiver.FileInfo{
|
|
||||||
FileInfo: info,
|
|
||||||
CustomName: path.Join("data", "attachments", objPath),
|
|
||||||
},
|
|
||||||
ReadCloser: object,
|
|
||||||
})
|
|
||||||
}); err != nil {
|
|
||||||
fatal("Failed to dump attachments: %v", err)
|
|
||||||
}
|
}
|
||||||
|
if err := z.AddFile("gitea-db.sql", dbDump); err != nil {
|
||||||
// Doesn't check if LogRootPath exists before processing --skip-log intentionally,
|
log.Fatalf("Failed to include gitea-db.sql: %v", err)
|
||||||
// ensuring that it's clear the dump is skipped whether the directory's initialized
|
}
|
||||||
// yet or not.
|
customDir, err := os.Stat(setting.CustomPath)
|
||||||
if ctx.IsSet("skip-log") && ctx.Bool("skip-log") {
|
if err == nil && customDir.IsDir() {
|
||||||
log.Info("Skip dumping log files")
|
if err := z.AddDir("custom", setting.CustomPath); err != nil {
|
||||||
|
log.Fatalf("Failed to include custom: %v", err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
isExist, err := util.IsExist(setting.LogRootPath)
|
log.Printf("Custom dir %s doesn't exist, skipped", setting.CustomPath)
|
||||||
if err != nil {
|
}
|
||||||
log.Error("Unable to check if %s exists. Error: %v", setting.LogRootPath, err)
|
|
||||||
|
if com.IsExist(setting.AppDataPath) {
|
||||||
|
log.Printf("Packing data directory...%s", setting.AppDataPath)
|
||||||
|
|
||||||
|
var sessionAbsPath string
|
||||||
|
if setting.SessionConfig.Provider == "file" {
|
||||||
|
sessionAbsPath = setting.SessionConfig.ProviderConfig
|
||||||
}
|
}
|
||||||
if isExist {
|
if err := zipAddDirectoryExclude(z, "data", setting.AppDataPath, sessionAbsPath); err != nil {
|
||||||
if err := addRecursiveExclude(w, "log", setting.LogRootPath, []string{absFileName}, verbose); err != nil {
|
log.Fatalf("Failed to include data directory: %v", err)
|
||||||
fatal("Failed to include log: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if fileName != "-" {
|
if err := z.AddDir("log", setting.LogRootPath); err != nil {
|
||||||
if err = w.Close(); err != nil {
|
log.Fatalf("Failed to include log: %v", err)
|
||||||
_ = 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 fileName != "-" {
|
if err = z.Close(); err != nil {
|
||||||
log.Info("Finish dumping in file %s", fileName)
|
_ = os.Remove(fileName)
|
||||||
} else {
|
log.Fatalf("Failed to save %s: %v", fileName, err)
|
||||||
log.Info("Finish dumping to stdout")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := os.Chmod(fileName, 0600); err != nil {
|
||||||
|
log.Printf("Can't change file access permissions mask to 0600: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func contains(slice []string, s string) bool {
|
// zipAddDirectoryExclude zips absPath to specified zipPath inside z excluding excludeAbsPath
|
||||||
for _, v := range slice {
|
func zipAddDirectoryExclude(zip *zip.ZipArchive, zipPath, absPath string, excludeAbsPath string) error {
|
||||||
if v == s {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// addRecursiveExclude zips absPath to specified insidePath inside writer excluding excludeAbsPath
|
|
||||||
func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeAbsPath []string, verbose bool) error {
|
|
||||||
absPath, err := filepath.Abs(absPath)
|
absPath, err := filepath.Abs(absPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -399,24 +168,24 @@ func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeA
|
||||||
}
|
}
|
||||||
defer dir.Close()
|
defer dir.Close()
|
||||||
|
|
||||||
|
zip.AddEmptyDir(zipPath)
|
||||||
|
|
||||||
files, err := dir.Readdir(0)
|
files, err := dir.Readdir(0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
currentAbsPath := path.Join(absPath, file.Name())
|
currentAbsPath := path.Join(absPath, file.Name())
|
||||||
currentInsidePath := path.Join(insidePath, file.Name())
|
currentZipPath := path.Join(zipPath, file.Name())
|
||||||
if file.IsDir() {
|
if file.IsDir() {
|
||||||
if !contains(excludeAbsPath, currentAbsPath) {
|
if currentAbsPath != excludeAbsPath {
|
||||||
if err := addFile(w, currentInsidePath, currentAbsPath, false); err != nil {
|
if err = zipAddDirectoryExclude(zip, currentZipPath, currentAbsPath, excludeAbsPath); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = addRecursiveExclude(w, currentInsidePath, currentAbsPath, excludeAbsPath, verbose); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if err = addFile(w, currentInsidePath, currentAbsPath, verbose); err != nil {
|
if err = zip.AddFile(currentZipPath, currentAbsPath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
162
cmd/dump_repo.go
162
cmd/dump_repo.go
|
@ -1,162 +0,0 @@
|
||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/convert"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/migrations"
|
|
||||||
"code.gitea.io/gitea/modules/migrations/base"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
"code.gitea.io/gitea/modules/structs"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CmdDumpRepository represents the available dump repository sub-command.
|
|
||||||
var CmdDumpRepository = cli.Command{
|
|
||||||
Name: "dump-repo",
|
|
||||||
Usage: "Dump the repository from git/github/gitea/gitlab",
|
|
||||||
Description: "This is a command for dumping the repository data.",
|
|
||||||
Action: runDumpRepository,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "git_service",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Git service, git, github, gitea, gitlab. If clone_addr could be recognized, this could be ignored.",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "repo_dir, r",
|
|
||||||
Value: "./data",
|
|
||||||
Usage: "Repository dir path to store the data",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "clone_addr",
|
|
||||||
Value: "",
|
|
||||||
Usage: "The URL will be clone, currently could be a git/github/gitea/gitlab http/https URL",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "auth_username",
|
|
||||||
Value: "",
|
|
||||||
Usage: "The username to visit the clone_addr",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "auth_password",
|
|
||||||
Value: "",
|
|
||||||
Usage: "The password to visit the clone_addr",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "auth_token",
|
|
||||||
Value: "",
|
|
||||||
Usage: "The personal token to visit the clone_addr",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "owner_name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "The data will be stored on a directory with owner name if not empty",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "repo_name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "The data will be stored on a directory with repository name if not empty",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "units",
|
|
||||||
Value: "",
|
|
||||||
Usage: `Which items will be migrated, one or more units should be separated as comma.
|
|
||||||
wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func runDumpRepository(ctx *cli.Context) error {
|
|
||||||
if err := initDB(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("AppPath: %s", setting.AppPath)
|
|
||||||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
|
||||||
log.Info("Custom path: %s", setting.CustomPath)
|
|
||||||
log.Info("Log path: %s", setting.LogRootPath)
|
|
||||||
setting.InitDBConfig()
|
|
||||||
|
|
||||||
var (
|
|
||||||
serviceType structs.GitServiceType
|
|
||||||
cloneAddr = ctx.String("clone_addr")
|
|
||||||
serviceStr = ctx.String("git_service")
|
|
||||||
)
|
|
||||||
|
|
||||||
if strings.HasPrefix(strings.ToLower(cloneAddr), "https://github.com/") {
|
|
||||||
serviceStr = "github"
|
|
||||||
} else if strings.HasPrefix(strings.ToLower(cloneAddr), "https://gitlab.com/") {
|
|
||||||
serviceStr = "gitlab"
|
|
||||||
} else if strings.HasPrefix(strings.ToLower(cloneAddr), "https://gitea.com/") {
|
|
||||||
serviceStr = "gitea"
|
|
||||||
}
|
|
||||||
if serviceStr == "" {
|
|
||||||
return errors.New("git_service missed or clone_addr cannot be recognized")
|
|
||||||
}
|
|
||||||
serviceType = convert.ToGitServiceType(serviceStr)
|
|
||||||
|
|
||||||
var opts = base.MigrateOptions{
|
|
||||||
GitServiceType: serviceType,
|
|
||||||
CloneAddr: cloneAddr,
|
|
||||||
AuthUsername: ctx.String("auth_username"),
|
|
||||||
AuthPassword: ctx.String("auth_password"),
|
|
||||||
AuthToken: ctx.String("auth_token"),
|
|
||||||
RepoName: ctx.String("repo_name"),
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ctx.String("units")) == 0 {
|
|
||||||
opts.Wiki = true
|
|
||||||
opts.Issues = true
|
|
||||||
opts.Milestones = true
|
|
||||||
opts.Labels = true
|
|
||||||
opts.Releases = true
|
|
||||||
opts.Comments = true
|
|
||||||
opts.PullRequests = true
|
|
||||||
opts.ReleaseAssets = true
|
|
||||||
} else {
|
|
||||||
units := strings.Split(ctx.String("units"), ",")
|
|
||||||
for _, unit := range units {
|
|
||||||
switch strings.ToLower(unit) {
|
|
||||||
case "wiki":
|
|
||||||
opts.Wiki = true
|
|
||||||
case "issues":
|
|
||||||
opts.Issues = true
|
|
||||||
case "milestones":
|
|
||||||
opts.Milestones = true
|
|
||||||
case "labels":
|
|
||||||
opts.Labels = true
|
|
||||||
case "releases":
|
|
||||||
opts.Releases = true
|
|
||||||
case "release_assets":
|
|
||||||
opts.ReleaseAssets = true
|
|
||||||
case "comments":
|
|
||||||
opts.Comments = true
|
|
||||||
case "pull_requests":
|
|
||||||
opts.PullRequests = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := migrations.DumpRepository(
|
|
||||||
context.Background(),
|
|
||||||
ctx.String("repo_dir"),
|
|
||||||
ctx.String("owner_name"),
|
|
||||||
opts,
|
|
||||||
); err != nil {
|
|
||||||
log.Fatal("Failed to dump repository: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Trace("Dump finished!!!")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
334
cmd/embedded.go
334
cmd/embedded.go
|
@ -1,334 +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.
|
|
||||||
|
|
||||||
//go:build bindata
|
|
||||||
// +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"
|
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
|
|
||||||
"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 := util.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,31 +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.
|
|
||||||
|
|
||||||
//go:build !bindata
|
|
||||||
// +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
|
|
||||||
}
|
|
101
cmd/generate.go
101
cmd/generate.go
|
@ -1,101 +0,0 @@
|
||||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
|
||||||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/generate"
|
|
||||||
|
|
||||||
"github.com/mattn/go-isatty"
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// CmdGenerate represents the available generate sub-command.
|
|
||||||
CmdGenerate = cli.Command{
|
|
||||||
Name: "generate",
|
|
||||||
Usage: "Command line interface for running generators",
|
|
||||||
Subcommands: []cli.Command{
|
|
||||||
subcmdSecret,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
subcmdSecret = cli.Command{
|
|
||||||
Name: "secret",
|
|
||||||
Usage: "Generate a secret token",
|
|
||||||
Subcommands: []cli.Command{
|
|
||||||
microcmdGenerateInternalToken,
|
|
||||||
microcmdGenerateLfsJwtSecret,
|
|
||||||
microcmdGenerateSecretKey,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdGenerateInternalToken = cli.Command{
|
|
||||||
Name: "INTERNAL_TOKEN",
|
|
||||||
Usage: "Generate a new INTERNAL_TOKEN",
|
|
||||||
Action: runGenerateInternalToken,
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdGenerateLfsJwtSecret = cli.Command{
|
|
||||||
Name: "JWT_SECRET",
|
|
||||||
Aliases: []string{"LFS_JWT_SECRET"},
|
|
||||||
Usage: "Generate a new JWT_SECRET",
|
|
||||||
Action: runGenerateLfsJwtSecret,
|
|
||||||
}
|
|
||||||
|
|
||||||
microcmdGenerateSecretKey = cli.Command{
|
|
||||||
Name: "SECRET_KEY",
|
|
||||||
Usage: "Generate a new SECRET_KEY",
|
|
||||||
Action: runGenerateSecretKey,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func runGenerateInternalToken(c *cli.Context) error {
|
|
||||||
internalToken, err := generate.NewInternalToken()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("%s", internalToken)
|
|
||||||
|
|
||||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
|
||||||
fmt.Printf("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func runGenerateLfsJwtSecret(c *cli.Context) error {
|
|
||||||
JWTSecretBase64, err := generate.NewJwtSecretBase64()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("%s", JWTSecretBase64)
|
|
||||||
|
|
||||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
|
||||||
fmt.Printf("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func runGenerateSecretKey(c *cli.Context) error {
|
|
||||||
secretKey, err := generate.NewSecretKey()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("%s", secretKey)
|
|
||||||
|
|
||||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
|
||||||
fmt.Printf("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
786
cmd/hook.go
786
cmd/hook.go
|
@ -8,37 +8,37 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
|
"code.gitea.io/git"
|
||||||
"code.gitea.io/gitea/models"
|
"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/private"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
hookBatchSize = 30
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// CmdHook represents the available hooks sub-command.
|
// CmdHook represents the available hooks sub-command.
|
||||||
CmdHook = cli.Command{
|
CmdHook = cli.Command{
|
||||||
Name: "hook",
|
Name: "hook",
|
||||||
Usage: "Delegate commands to corresponding Git hooks",
|
Usage: "Delegate commands to corresponding Git hooks",
|
||||||
Description: "This should only be called by Git",
|
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{
|
Subcommands: []cli.Command{
|
||||||
subcmdHookPreReceive,
|
subcmdHookPreReceive,
|
||||||
subcmdHookUpdate,
|
subcmdHookUpadte,
|
||||||
subcmdHookPostReceive,
|
subcmdHookPostReceive,
|
||||||
subcmdHookProcReceive,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,183 +47,54 @@ var (
|
||||||
Usage: "Delegate pre-receive Git hook",
|
Usage: "Delegate pre-receive Git hook",
|
||||||
Description: "This command should only be called by Git",
|
Description: "This command should only be called by Git",
|
||||||
Action: runHookPreReceive,
|
Action: runHookPreReceive,
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "debug",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
subcmdHookUpdate = cli.Command{
|
subcmdHookUpadte = cli.Command{
|
||||||
Name: "update",
|
Name: "update",
|
||||||
Usage: "Delegate update Git hook",
|
Usage: "Delegate update Git hook",
|
||||||
Description: "This command should only be called by Git",
|
Description: "This command should only be called by Git",
|
||||||
Action: runHookUpdate,
|
Action: runHookUpdate,
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "debug",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
subcmdHookPostReceive = cli.Command{
|
subcmdHookPostReceive = cli.Command{
|
||||||
Name: "post-receive",
|
Name: "post-receive",
|
||||||
Usage: "Delegate post-receive Git hook",
|
Usage: "Delegate post-receive Git hook",
|
||||||
Description: "This command should only be called by Git",
|
Description: "This command should only be called by Git",
|
||||||
Action: runHookPostReceive,
|
Action: runHookPostReceive,
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "debug",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
// Note: new hook since git 2.29
|
|
||||||
subcmdHookProcReceive = cli.Command{
|
|
||||||
Name: "proc-receive",
|
|
||||||
Usage: "Delegate proc-receive Git hook",
|
|
||||||
Description: "This command should only be called by Git",
|
|
||||||
Action: runHookProcReceive,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "debug",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
type delayWriter struct {
|
func hookSetup(logPath string) {
|
||||||
internal io.Writer
|
setting.NewContext()
|
||||||
buf *bytes.Buffer
|
log.NewGitLogger(filepath.Join(setting.LogRootPath, logPath))
|
||||||
timer *time.Timer
|
models.LoadConfigs()
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
func runHookPreReceive(c *cli.Context) error {
|
||||||
if os.Getenv(models.EnvIsInternal) == "true" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
setup("hooks/pre-receive.log", c.Bool("debug"))
|
|
||||||
|
|
||||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||||
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
|
||||||
return fail(`Rejecting changes as Gitea environment not set.
|
|
||||||
If you are pushing over SSH you must push with a key managed by
|
|
||||||
Gitea or set your environment appropriately.`, "")
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// the environment is set by serv command
|
if c.IsSet("config") {
|
||||||
isWiki := os.Getenv(models.EnvRepoIsWiki) == "true"
|
setting.CustomConf = c.String("config")
|
||||||
|
} else if c.GlobalIsSet("config") {
|
||||||
|
setting.CustomConf = c.GlobalString("config")
|
||||||
|
}
|
||||||
|
|
||||||
|
hookSetup("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)
|
username := os.Getenv(models.EnvRepoUsername)
|
||||||
reponame := os.Getenv(models.EnvRepoName)
|
reponame := os.Getenv(models.EnvRepoName)
|
||||||
userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
userIDStr := os.Getenv(models.EnvPusherID)
|
||||||
prID, _ := strconv.ParseInt(os.Getenv(models.EnvPRID), 10, 64)
|
repoPath := models.RepoPath(username, reponame)
|
||||||
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(),
|
|
||||||
PullRequestID: prID,
|
|
||||||
IsDeployKey: isDeployKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
supportProcRecive := false
|
|
||||||
if git.CheckGitVersionAtLeast("2.29") == nil {
|
|
||||||
supportProcRecive = true
|
|
||||||
}
|
|
||||||
|
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
|
buf.Write(scanner.Bytes())
|
||||||
|
buf.WriteByte('\n')
|
||||||
|
|
||||||
// TODO: support news feeds for wiki
|
// TODO: support news feeds for wiki
|
||||||
if isWiki {
|
if isWiki {
|
||||||
continue
|
continue
|
||||||
|
@ -237,138 +108,84 @@ Gitea or set your environment appropriately.`, "")
|
||||||
oldCommitID := string(fields[0])
|
oldCommitID := string(fields[0])
|
||||||
newCommitID := string(fields[1])
|
newCommitID := string(fields[1])
|
||||||
refFullName := string(fields[2])
|
refFullName := string(fields[2])
|
||||||
total++
|
|
||||||
lastline++
|
|
||||||
|
|
||||||
// If the ref is a branch or tag, check if it's protected
|
branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
|
||||||
// if supportProcRecive all ref should be checked because
|
protectBranch, err := private.GetProtectedBranchBy(repoID, branchName)
|
||||||
// permission check was delayed
|
if err != nil {
|
||||||
if supportProcRecive || strings.HasPrefix(refFullName, git.BranchPrefix) || strings.HasPrefix(refFullName, git.TagPrefix) {
|
log.GitLogger.Fatal(2, "retrieve protected branches information failed")
|
||||||
oldCommitIDs[count] = oldCommitID
|
}
|
||||||
newCommitIDs[count] = newCommitID
|
|
||||||
refFullNames[count] = refFullName
|
|
||||||
count++
|
|
||||||
fmt.Fprintf(out, "*")
|
|
||||||
|
|
||||||
if count >= hookBatchSize {
|
if protectBranch != nil && protectBranch.IsProtected() {
|
||||||
fmt.Fprintf(out, " Checking %d references\n", count)
|
// detect force push
|
||||||
|
if git.EmptySHA != oldCommitID {
|
||||||
hookOptions.OldCommitIDs = oldCommitIDs
|
output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+newCommitID).RunInDir(repoPath)
|
||||||
hookOptions.NewCommitIDs = newCommitIDs
|
if err != nil {
|
||||||
hookOptions.RefFullNames = refFullNames
|
fail("Internal error", "Fail to detect force push: %v", err)
|
||||||
statusCode, msg := private.HookPreReceive(ctx, username, reponame, hookOptions)
|
} else if len(output) > 0 {
|
||||||
switch statusCode {
|
fail(fmt.Sprintf("branch %s is protected from force push", branchName), "")
|
||||||
case http.StatusOK:
|
}
|
||||||
// no-op
|
}
|
||||||
case http.StatusInternalServerError:
|
|
||||||
return fail("Internal Server Error", msg)
|
// check and deletion
|
||||||
default:
|
if newCommitID == git.EmptySHA {
|
||||||
return fail(msg, "")
|
fail(fmt.Sprintf("branch %s is protected from deletion", branchName), "")
|
||||||
|
} else {
|
||||||
|
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), "")
|
||||||
}
|
}
|
||||||
count = 0
|
|
||||||
lastline = 0
|
|
||||||
}
|
}
|
||||||
} 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 references\n", count)
|
|
||||||
|
|
||||||
statusCode, msg := private.HookPreReceive(ctx, username, reponame, hookOptions)
|
|
||||||
switch statusCode {
|
|
||||||
case http.StatusInternalServerError:
|
|
||||||
return fail("Internal Server Error", msg)
|
|
||||||
case http.StatusForbidden:
|
|
||||||
return fail(msg, "")
|
|
||||||
}
|
|
||||||
} else if lastline > 0 {
|
|
||||||
fmt.Fprintf(out, "\n")
|
|
||||||
lastline = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(out, "Checked %d references in total\n", total)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runHookUpdate(c *cli.Context) error {
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
hookSetup("hooks/update.log")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runHookPostReceive(c *cli.Context) error {
|
func runHookPostReceive(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
// First of all run update-server-info no matter what
|
|
||||||
if _, err := git.NewCommand("update-server-info").SetParentContext(ctx).Run(); err != nil {
|
|
||||||
return fmt.Errorf("Failed to call 'git update-server-info': %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now if we're an internal don't do anything else
|
|
||||||
if os.Getenv(models.EnvIsInternal) == "true" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
setup("hooks/post-receive.log", c.Bool("debug"))
|
|
||||||
|
|
||||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||||
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
|
||||||
return fail(`Rejecting changes as Gitea environment not set.
|
|
||||||
If you are pushing over SSH you must push with a key managed by
|
|
||||||
Gitea or set your environment appropriately.`, "")
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var out io.Writer
|
if c.IsSet("config") {
|
||||||
var dWriter *delayWriter
|
setting.CustomConf = c.String("config")
|
||||||
out = &nilWriter{}
|
} else if c.GlobalIsSet("config") {
|
||||||
if setting.Git.VerbosePush {
|
setting.CustomConf = c.GlobalString("config")
|
||||||
if setting.Git.VerbosePushDelay > 0 {
|
|
||||||
dWriter = newDelayWriter(os.Stdout, setting.Git.VerbosePushDelay)
|
|
||||||
defer dWriter.Close()
|
|
||||||
out = dWriter
|
|
||||||
} else {
|
|
||||||
out = os.Stdout
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// the environment is set by serv command
|
hookSetup("hooks/post-receive.log")
|
||||||
|
|
||||||
|
// the environment setted on serv command
|
||||||
repoUser := os.Getenv(models.EnvRepoUsername)
|
repoUser := os.Getenv(models.EnvRepoUsername)
|
||||||
isWiki := os.Getenv(models.EnvRepoIsWiki) == "true"
|
isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
|
||||||
repoName := os.Getenv(models.EnvRepoName)
|
repoName := os.Getenv(models.EnvRepoName)
|
||||||
pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||||
pusherName := os.Getenv(models.EnvPusherName)
|
pusherName := os.Getenv(models.EnvPusherName)
|
||||||
|
|
||||||
hookOptions := private.HookOptions{
|
buf := bytes.NewBuffer(nil)
|
||||||
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)
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
|
buf.Write(scanner.Bytes())
|
||||||
|
buf.WriteByte('\n')
|
||||||
|
|
||||||
// TODO: support news feeds for wiki
|
// TODO: support news feeds for wiki
|
||||||
if isWiki {
|
if isWiki {
|
||||||
continue
|
continue
|
||||||
|
@ -379,430 +196,21 @@ Gitea or set your environment appropriately.`, "")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(out, ".")
|
oldCommitID := string(fields[0])
|
||||||
oldCommitIDs[count] = string(fields[0])
|
newCommitID := string(fields[1])
|
||||||
newCommitIDs[count] = string(fields[1])
|
refFullName := string(fields[2])
|
||||||
refFullNames[count] = string(fields[2])
|
|
||||||
if refFullNames[count] == git.BranchPrefix+"master" && newCommitIDs[count] != git.EmptySHA && count == total {
|
if err := private.PushUpdate(models.PushUpdateOptions{
|
||||||
masterPushed = true
|
RefFullName: refFullName,
|
||||||
|
OldCommitID: oldCommitID,
|
||||||
|
NewCommitID: newCommitID,
|
||||||
|
PusherID: pusherID,
|
||||||
|
PusherName: pusherName,
|
||||||
|
RepoUserName: repoUser,
|
||||||
|
RepoName: repoName,
|
||||||
|
}); err != nil {
|
||||||
|
log.GitLogger.Error(2, "Update: %v", err)
|
||||||
}
|
}
|
||||||
count++
|
|
||||||
total++
|
|
||||||
|
|
||||||
if count >= hookBatchSize {
|
|
||||||
fmt.Fprintf(out, " Processing %d references\n", count)
|
|
||||||
hookOptions.OldCommitIDs = oldCommitIDs
|
|
||||||
hookOptions.NewCommitIDs = newCommitIDs
|
|
||||||
hookOptions.RefFullNames = refFullNames
|
|
||||||
resp, err := private.HookPostReceive(ctx, repoUser, repoName, hookOptions)
|
|
||||||
if resp == nil {
|
|
||||||
_ = dWriter.Close()
|
|
||||||
hookPrintResults(results)
|
|
||||||
return fail("Internal Server Error", err)
|
|
||||||
}
|
|
||||||
wasEmpty = wasEmpty || resp.RepoWasEmpty
|
|
||||||
results = append(results, resp.Results...)
|
|
||||||
count = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if count == 0 {
|
|
||||||
if wasEmpty && masterPushed {
|
|
||||||
// We need to tell the repo to reset the default branch to master
|
|
||||||
err := private.SetDefaultBranch(ctx, repoUser, repoName, "master")
|
|
||||||
if err != nil {
|
|
||||||
return fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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(ctx, repoUser, repoName, hookOptions)
|
|
||||||
if resp == nil {
|
|
||||||
_ = dWriter.Close()
|
|
||||||
hookPrintResults(results)
|
|
||||||
return 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(ctx, repoUser, repoName, "master")
|
|
||||||
if err != nil {
|
|
||||||
return 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
|
|
||||||
}
|
|
||||||
|
|
||||||
func runHookProcReceive(c *cli.Context) error {
|
|
||||||
setup("hooks/proc-receive.log", c.Bool("debug"))
|
|
||||||
|
|
||||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
|
||||||
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
|
||||||
return fail(`Rejecting changes as Gitea environment not set.
|
|
||||||
If you are pushing over SSH you must push with a key managed by
|
|
||||||
Gitea or set your environment appropriately.`, "")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if git.CheckGitVersionAtLeast("2.29") != nil {
|
|
||||||
return fail("Internal Server Error", "git not support proc-receive.")
|
|
||||||
}
|
|
||||||
|
|
||||||
reader := bufio.NewReader(os.Stdin)
|
|
||||||
repoUser := os.Getenv(models.EnvRepoUsername)
|
|
||||||
repoName := os.Getenv(models.EnvRepoName)
|
|
||||||
pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
|
||||||
pusherName := os.Getenv(models.EnvPusherName)
|
|
||||||
|
|
||||||
// 1. Version and features negotiation.
|
|
||||||
// S: PKT-LINE(version=1\0push-options atomic...) / PKT-LINE(version=1\n)
|
|
||||||
// S: flush-pkt
|
|
||||||
// H: PKT-LINE(version=1\0push-options...)
|
|
||||||
// H: flush-pkt
|
|
||||||
|
|
||||||
rs, err := readPktLine(reader, pktLineTypeData)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
const VersionHead string = "version=1"
|
|
||||||
|
|
||||||
var (
|
|
||||||
hasPushOptions bool
|
|
||||||
response = []byte(VersionHead)
|
|
||||||
requestOptions []string
|
|
||||||
)
|
|
||||||
|
|
||||||
index := bytes.IndexByte(rs.Data, byte(0))
|
|
||||||
if index >= len(rs.Data) {
|
|
||||||
return fail("Internal Server Error", "pkt-line: format error "+fmt.Sprint(rs.Data))
|
|
||||||
}
|
|
||||||
|
|
||||||
if index < 0 {
|
|
||||||
if len(rs.Data) == 10 && rs.Data[9] == '\n' {
|
|
||||||
index = 9
|
|
||||||
} else {
|
|
||||||
return fail("Internal Server Error", "pkt-line: format error "+fmt.Sprint(rs.Data))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if string(rs.Data[0:index]) != VersionHead {
|
|
||||||
return fail("Internal Server Error", "Received unsupported version: %s", string(rs.Data[0:index]))
|
|
||||||
}
|
|
||||||
requestOptions = strings.Split(string(rs.Data[index+1:]), " ")
|
|
||||||
|
|
||||||
for _, option := range requestOptions {
|
|
||||||
if strings.HasPrefix(option, "push-options") {
|
|
||||||
response = append(response, byte(0))
|
|
||||||
response = append(response, []byte("push-options")...)
|
|
||||||
hasPushOptions = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
response = append(response, '\n')
|
|
||||||
|
|
||||||
_, err = readPktLine(reader, pktLineTypeFlush)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = writeDataPktLine(os.Stdout, response)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = writeFlushPktLine(os.Stdout)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. receive commands from server.
|
|
||||||
// S: PKT-LINE(<old-oid> <new-oid> <ref>)
|
|
||||||
// S: ... ...
|
|
||||||
// S: flush-pkt
|
|
||||||
// # [receive push-options]
|
|
||||||
// S: PKT-LINE(push-option)
|
|
||||||
// S: ... ...
|
|
||||||
// S: flush-pkt
|
|
||||||
hookOptions := private.HookOptions{
|
|
||||||
UserName: pusherName,
|
|
||||||
UserID: pusherID,
|
|
||||||
}
|
|
||||||
hookOptions.OldCommitIDs = make([]string, 0, hookBatchSize)
|
|
||||||
hookOptions.NewCommitIDs = make([]string, 0, hookBatchSize)
|
|
||||||
hookOptions.RefFullNames = make([]string, 0, hookBatchSize)
|
|
||||||
|
|
||||||
for {
|
|
||||||
// note: pktLineTypeUnknow means pktLineTypeFlush and pktLineTypeData all allowed
|
|
||||||
rs, err = readPktLine(reader, pktLineTypeUnknow)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if rs.Type == pktLineTypeFlush {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
t := strings.SplitN(string(rs.Data), " ", 3)
|
|
||||||
if len(t) != 3 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
hookOptions.OldCommitIDs = append(hookOptions.OldCommitIDs, t[0])
|
|
||||||
hookOptions.NewCommitIDs = append(hookOptions.NewCommitIDs, t[1])
|
|
||||||
hookOptions.RefFullNames = append(hookOptions.RefFullNames, t[2])
|
|
||||||
}
|
|
||||||
|
|
||||||
hookOptions.GitPushOptions = make(map[string]string)
|
|
||||||
|
|
||||||
if hasPushOptions {
|
|
||||||
for {
|
|
||||||
rs, err = readPktLine(reader, pktLineTypeUnknow)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if rs.Type == pktLineTypeFlush {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
kv := strings.SplitN(string(rs.Data), "=", 2)
|
|
||||||
if len(kv) == 2 {
|
|
||||||
hookOptions.GitPushOptions[kv[0]] = kv[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. run hook
|
|
||||||
resp, err := private.HookProcReceive(ctx, repoUser, repoName, hookOptions)
|
|
||||||
if err != nil {
|
|
||||||
return fail("Internal Server Error", "run proc-receive hook failed :%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. response result to service
|
|
||||||
// # a. OK, but has an alternate reference. The alternate reference name
|
|
||||||
// # and other status can be given in option directives.
|
|
||||||
// H: PKT-LINE(ok <ref>)
|
|
||||||
// H: PKT-LINE(option refname <refname>)
|
|
||||||
// H: PKT-LINE(option old-oid <old-oid>)
|
|
||||||
// H: PKT-LINE(option new-oid <new-oid>)
|
|
||||||
// H: PKT-LINE(option forced-update)
|
|
||||||
// H: ... ...
|
|
||||||
// H: flush-pkt
|
|
||||||
// # b. NO, I reject it.
|
|
||||||
// H: PKT-LINE(ng <ref> <reason>)
|
|
||||||
// # c. Fall through, let 'receive-pack' to execute it.
|
|
||||||
// H: PKT-LINE(ok <ref>)
|
|
||||||
// H: PKT-LINE(option fall-through)
|
|
||||||
|
|
||||||
for _, rs := range resp.Results {
|
|
||||||
if len(rs.Err) > 0 {
|
|
||||||
err = writeDataPktLine(os.Stdout, []byte("ng "+rs.OriginalRef+" "+rs.Err))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if rs.IsNotMatched {
|
|
||||||
err = writeDataPktLine(os.Stdout, []byte("ok "+rs.OriginalRef))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = writeDataPktLine(os.Stdout, []byte("option fall-through"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
err = writeDataPktLine(os.Stdout, []byte("ok "+rs.OriginalRef))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = writeDataPktLine(os.Stdout, []byte("option refname "+rs.Ref))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rs.OldOID != git.EmptySHA {
|
|
||||||
err = writeDataPktLine(os.Stdout, []byte("option old-oid "+rs.OldOID))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = writeDataPktLine(os.Stdout, []byte("option new-oid "+rs.NewOID))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rs.IsForcePush {
|
|
||||||
err = writeDataPktLine(os.Stdout, []byte("option forced-update"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = writeFlushPktLine(os.Stdout)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// git PKT-Line api
|
|
||||||
// pktLineType message type of pkt-line
|
|
||||||
type pktLineType int64
|
|
||||||
|
|
||||||
const (
|
|
||||||
// UnKnow type
|
|
||||||
pktLineTypeUnknow pktLineType = 0
|
|
||||||
// flush-pkt "0000"
|
|
||||||
pktLineTypeFlush pktLineType = iota
|
|
||||||
// data line
|
|
||||||
pktLineTypeData
|
|
||||||
)
|
|
||||||
|
|
||||||
// gitPktLine pkt-line api
|
|
||||||
type gitPktLine struct {
|
|
||||||
Type pktLineType
|
|
||||||
Length uint64
|
|
||||||
Data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func readPktLine(in *bufio.Reader, requestType pktLineType) (*gitPktLine, error) {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
r *gitPktLine
|
|
||||||
)
|
|
||||||
|
|
||||||
// read prefix
|
|
||||||
lengthBytes := make([]byte, 4)
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
lengthBytes[i], err = in.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fail("Internal Server Error", "Pkt-Line: read stdin failed : %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r = new(gitPktLine)
|
|
||||||
r.Length, err = strconv.ParseUint(string(lengthBytes), 16, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fail("Internal Server Error", "Pkt-Line format is wrong :%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Length == 0 {
|
|
||||||
if requestType == pktLineTypeData {
|
|
||||||
return nil, fail("Internal Server Error", "Pkt-Line format is wrong")
|
|
||||||
}
|
|
||||||
r.Type = pktLineTypeFlush
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Length <= 4 || r.Length > 65520 || requestType == pktLineTypeFlush {
|
|
||||||
return nil, fail("Internal Server Error", "Pkt-Line format is wrong")
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Data = make([]byte, r.Length-4)
|
|
||||||
for i := range r.Data {
|
|
||||||
r.Data[i], err = in.ReadByte()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fail("Internal Server Error", "Pkt-Line: read stdin failed : %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Type = pktLineTypeData
|
|
||||||
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeFlushPktLine(out io.Writer) error {
|
|
||||||
l, err := out.Write([]byte("0000"))
|
|
||||||
if err != nil {
|
|
||||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
|
||||||
}
|
|
||||||
if l != 4 {
|
|
||||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeDataPktLine(out io.Writer, data []byte) error {
|
|
||||||
hexchar := []byte("0123456789abcdef")
|
|
||||||
hex := func(n uint64) byte {
|
|
||||||
return hexchar[(n)&15]
|
|
||||||
}
|
|
||||||
|
|
||||||
length := uint64(len(data) + 4)
|
|
||||||
tmp := make([]byte, 4)
|
|
||||||
tmp[0] = hex(length >> 12)
|
|
||||||
tmp[1] = hex(length >> 8)
|
|
||||||
tmp[2] = hex(length >> 4)
|
|
||||||
tmp[3] = hex(length)
|
|
||||||
|
|
||||||
lr, err := out.Write(tmp)
|
|
||||||
if err != nil {
|
|
||||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
|
||||||
}
|
|
||||||
if 4 != lr {
|
|
||||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
lr, err = out.Write(data)
|
|
||||||
if err != nil {
|
|
||||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
|
||||||
}
|
|
||||||
if int(length-4) != lr {
|
|
||||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPktLine(t *testing.T) {
|
|
||||||
// test read
|
|
||||||
s := strings.NewReader("0000")
|
|
||||||
r := bufio.NewReader(s)
|
|
||||||
result, err := readPktLine(r, pktLineTypeFlush)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, pktLineTypeFlush, result.Type)
|
|
||||||
|
|
||||||
s = strings.NewReader("0006a\n")
|
|
||||||
r = bufio.NewReader(s)
|
|
||||||
result, err = readPktLine(r, pktLineTypeData)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, pktLineTypeData, result.Type)
|
|
||||||
assert.Equal(t, []byte("a\n"), result.Data)
|
|
||||||
|
|
||||||
// test write
|
|
||||||
w := bytes.NewBuffer([]byte{})
|
|
||||||
err = writeFlushPktLine(w)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, []byte("0000"), w.Bytes())
|
|
||||||
|
|
||||||
w.Reset()
|
|
||||||
err = writeDataPktLine(w, []byte("a\nb"))
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, []byte("0007a\nb"), w.Bytes())
|
|
||||||
}
|
|
76
cmd/keys.go
76
cmd/keys.go
|
@ -1,76 +0,0 @@
|
||||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/private"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CmdKeys represents the available keys sub-command
|
|
||||||
var CmdKeys = cli.Command{
|
|
||||||
Name: "keys",
|
|
||||||
Usage: "This command queries the Gitea database to get the authorized command for a given ssh key fingerprint",
|
|
||||||
Action: runKeys,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "expected, e",
|
|
||||||
Value: "git",
|
|
||||||
Usage: "Expected user for whom provide key commands",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "username, u",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Username trying to log in by SSH",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "type, t",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Type of the SSH key provided to the SSH Server (requires content to be provided too)",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "content, k",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Base64 encoded content of the SSH key provided to the SSH Server (requires type to be provided too)",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func runKeys(c *cli.Context) error {
|
|
||||||
if !c.IsSet("username") {
|
|
||||||
return errors.New("No username provided")
|
|
||||||
}
|
|
||||||
// Check username matches the expected username
|
|
||||||
if strings.TrimSpace(c.String("username")) != strings.TrimSpace(c.String("expected")) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
content := ""
|
|
||||||
|
|
||||||
if c.IsSet("type") && c.IsSet("content") {
|
|
||||||
content = fmt.Sprintf("%s %s", strings.TrimSpace(c.String("type")), strings.TrimSpace(c.String("content")))
|
|
||||||
}
|
|
||||||
|
|
||||||
if content == "" {
|
|
||||||
return errors.New("No key type and content provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
setup("keys.log", false)
|
|
||||||
|
|
||||||
authorizedString, err := private.AuthorizedPublicKeyByContent(ctx, content)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println(strings.TrimSpace(authorizedString))
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/private"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
func runSendMail(c *cli.Context) error {
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
setting.NewContext()
|
|
||||||
|
|
||||||
if err := argsSet(c, "title"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
subject := c.String("title")
|
|
||||||
confirmSkiped := c.Bool("force")
|
|
||||||
body := c.String("content")
|
|
||||||
|
|
||||||
if !confirmSkiped {
|
|
||||||
if len(body) == 0 {
|
|
||||||
fmt.Print("warning: Content is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Print("Proceed with sending email? [Y/n] ")
|
|
||||||
isConfirmed, err := confirm()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
} else if !isConfirmed {
|
|
||||||
fmt.Println("The mail was not sent")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
status, message := private.SendEmail(ctx, subject, body, nil)
|
|
||||||
if status != http.StatusOK {
|
|
||||||
fmt.Printf("error: %s\n", message)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Success: %s\n", message)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
478
cmd/manager.go
478
cmd/manager.go
|
@ -1,478 +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()
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
statusCode, msg := private.RemoveLogger(ctx, group, name)
|
|
||||||
switch statusCode {
|
|
||||||
case http.StatusInternalServerError:
|
|
||||||
return fail("InternalServerError", msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func runAddSMTPLogger(c *cli.Context) error {
|
|
||||||
setup("manager", c.Bool("debug"))
|
|
||||||
vals := map[string]interface{}{}
|
|
||||||
mode := "smtp"
|
|
||||||
if c.IsSet("host") {
|
|
||||||
vals["host"] = c.String("host")
|
|
||||||
} else {
|
|
||||||
vals["host"] = "127.0.0.1:25"
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.IsSet("username") {
|
|
||||||
vals["username"] = c.String("username")
|
|
||||||
}
|
|
||||||
if c.IsSet("password") {
|
|
||||||
vals["password"] = c.String("password")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.IsSet("send-to") {
|
|
||||||
return fmt.Errorf("Some recipients must be provided")
|
|
||||||
}
|
|
||||||
vals["sendTos"] = c.StringSlice("send-to")
|
|
||||||
|
|
||||||
if c.IsSet("subject") {
|
|
||||||
vals["subject"] = c.String("subject")
|
|
||||||
} else {
|
|
||||||
vals["subject"] = "Diagnostic message from Gitea"
|
|
||||||
}
|
|
||||||
|
|
||||||
return commonAddLogger(c, mode, vals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func runAddConnLogger(c *cli.Context) error {
|
|
||||||
setup("manager", c.Bool("debug"))
|
|
||||||
vals := map[string]interface{}{}
|
|
||||||
mode := "conn"
|
|
||||||
vals["net"] = "tcp"
|
|
||||||
if c.IsSet("protocol") {
|
|
||||||
switch c.String("protocol") {
|
|
||||||
case "udp":
|
|
||||||
vals["net"] = "udp"
|
|
||||||
case "unix":
|
|
||||||
vals["net"] = "unix"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if c.IsSet("address") {
|
|
||||||
vals["address"] = c.String("address")
|
|
||||||
} else {
|
|
||||||
vals["address"] = ":7020"
|
|
||||||
}
|
|
||||||
if c.IsSet("reconnect") {
|
|
||||||
vals["reconnect"] = c.Bool("reconnect")
|
|
||||||
}
|
|
||||||
if c.IsSet("reconnect-on-message") {
|
|
||||||
vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
|
|
||||||
}
|
|
||||||
return commonAddLogger(c, mode, vals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func runAddFileLogger(c *cli.Context) error {
|
|
||||||
setup("manager", c.Bool("debug"))
|
|
||||||
vals := map[string]interface{}{}
|
|
||||||
mode := "file"
|
|
||||||
if c.IsSet("filename") {
|
|
||||||
vals["filename"] = c.String("filename")
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("filename must be set when creating a file logger")
|
|
||||||
}
|
|
||||||
if c.IsSet("rotate") {
|
|
||||||
vals["rotate"] = c.Bool("rotate")
|
|
||||||
}
|
|
||||||
if c.IsSet("max-size") {
|
|
||||||
vals["maxsize"] = c.Int64("max-size")
|
|
||||||
}
|
|
||||||
if c.IsSet("daily") {
|
|
||||||
vals["daily"] = c.Bool("daily")
|
|
||||||
}
|
|
||||||
if c.IsSet("max-days") {
|
|
||||||
vals["maxdays"] = c.Int("max-days")
|
|
||||||
}
|
|
||||||
if c.IsSet("compress") {
|
|
||||||
vals["compress"] = c.Bool("compress")
|
|
||||||
}
|
|
||||||
if c.IsSet("compression-level") {
|
|
||||||
vals["compressionLevel"] = c.Int("compression-level")
|
|
||||||
}
|
|
||||||
return commonAddLogger(c, mode, vals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func runAddConsoleLogger(c *cli.Context) error {
|
|
||||||
setup("manager", c.Bool("debug"))
|
|
||||||
vals := map[string]interface{}{}
|
|
||||||
mode := "console"
|
|
||||||
if c.IsSet("stderr") && c.Bool("stderr") {
|
|
||||||
vals["stderr"] = c.Bool("stderr")
|
|
||||||
}
|
|
||||||
return commonAddLogger(c, mode, vals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func commonAddLogger(c *cli.Context, mode string, vals map[string]interface{}) error {
|
|
||||||
if len(c.String("level")) > 0 {
|
|
||||||
vals["level"] = log.FromString(c.String("level")).String()
|
|
||||||
}
|
|
||||||
if len(c.String("stacktrace-level")) > 0 {
|
|
||||||
vals["stacktraceLevel"] = log.FromString(c.String("stacktrace-level")).String()
|
|
||||||
}
|
|
||||||
if len(c.String("expression")) > 0 {
|
|
||||||
vals["expression"] = c.String("expression")
|
|
||||||
}
|
|
||||||
if len(c.String("prefix")) > 0 {
|
|
||||||
vals["prefix"] = c.String("prefix")
|
|
||||||
}
|
|
||||||
if len(c.String("flags")) > 0 {
|
|
||||||
vals["flags"] = log.FlagsFromString(c.String("flags"))
|
|
||||||
}
|
|
||||||
if c.IsSet("color") {
|
|
||||||
vals["colorize"] = c.Bool("color")
|
|
||||||
}
|
|
||||||
group := "default"
|
|
||||||
if c.IsSet("group") {
|
|
||||||
group = c.String("group")
|
|
||||||
}
|
|
||||||
name := mode
|
|
||||||
if c.IsSet("name") {
|
|
||||||
name = c.String("name")
|
|
||||||
}
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
statusCode, msg := private.AddLogger(ctx, group, name, mode, vals)
|
|
||||||
switch statusCode {
|
|
||||||
case http.StatusInternalServerError:
|
|
||||||
return fail("InternalServerError", msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func runShutdown(c *cli.Context) error {
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
setup("manager", c.Bool("debug"))
|
|
||||||
statusCode, msg := private.Shutdown(ctx)
|
|
||||||
switch statusCode {
|
|
||||||
case http.StatusInternalServerError:
|
|
||||||
return fail("InternalServerError", msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func runRestart(c *cli.Context) error {
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
setup("manager", c.Bool("debug"))
|
|
||||||
statusCode, msg := private.Restart(ctx)
|
|
||||||
switch statusCode {
|
|
||||||
case http.StatusInternalServerError:
|
|
||||||
return fail("InternalServerError", msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func runFlushQueues(c *cli.Context) error {
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
setup("manager", c.Bool("debug"))
|
|
||||||
statusCode, msg := private.FlushQueues(ctx, c.Duration("timeout"), c.Bool("non-blocking"))
|
|
||||||
switch statusCode {
|
|
||||||
case http.StatusInternalServerError:
|
|
||||||
return fail("InternalServerError", msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func runPauseLogging(c *cli.Context) error {
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
setup("manager", c.Bool("debug"))
|
|
||||||
statusCode, msg := private.PauseLogging(ctx)
|
|
||||||
switch statusCode {
|
|
||||||
case http.StatusInternalServerError:
|
|
||||||
return fail("InternalServerError", msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func runResumeLogging(c *cli.Context) error {
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
setup("manager", c.Bool("debug"))
|
|
||||||
statusCode, msg := private.ResumeLogging(ctx)
|
|
||||||
switch statusCode {
|
|
||||||
case http.StatusInternalServerError:
|
|
||||||
return fail("InternalServerError", msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func runReleaseReopenLogging(c *cli.Context) error {
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
setup("manager", c.Bool("debug"))
|
|
||||||
statusCode, msg := private.ReleaseReopenLogging(ctx)
|
|
||||||
switch statusCode {
|
|
||||||
case http.StatusInternalServerError:
|
|
||||||
return fail("InternalServerError", msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(os.Stdout, msg)
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/models/migrations"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CmdMigrate represents the available migrate sub-command.
|
|
||||||
var CmdMigrate = cli.Command{
|
|
||||||
Name: "migrate",
|
|
||||||
Usage: "Migrate the database",
|
|
||||||
Description: "This is a command for migrating the database, so that you can run gitea admin create-user before starting the server.",
|
|
||||||
Action: runMigrate,
|
|
||||||
}
|
|
||||||
|
|
||||||
func runMigrate(ctx *cli.Context) error {
|
|
||||||
if err := initDB(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("AppPath: %s", setting.AppPath)
|
|
||||||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
|
||||||
log.Info("Custom path: %s", setting.CustomPath)
|
|
||||||
log.Info("Log path: %s", setting.LogRootPath)
|
|
||||||
setting.InitDBConfig()
|
|
||||||
|
|
||||||
if err := models.NewEngine(context.Background(), migrations.Migrate); err != nil {
|
|
||||||
log.Fatal("Failed to initialize ORM engine: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -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.Info("AppPath: %s", setting.AppPath)
|
|
||||||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
|
||||||
log.Info("Custom path: %s", setting.CustomPath)
|
|
||||||
log.Info("Log path: %s", setting.LogRootPath)
|
|
||||||
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 original placement.")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/private"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CmdRestoreRepository represents the available restore a repository sub-command.
|
|
||||||
var CmdRestoreRepository = cli.Command{
|
|
||||||
Name: "restore-repo",
|
|
||||||
Usage: "Restore the repository from disk",
|
|
||||||
Description: "This is a command for restoring the repository data.",
|
|
||||||
Action: runRestoreRepository,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "repo_dir, r",
|
|
||||||
Value: "./data",
|
|
||||||
Usage: "Repository dir path to restore from",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "owner_name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Restore destination owner name",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "repo_name",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Restore destination repository name",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "units",
|
|
||||||
Value: "",
|
|
||||||
Usage: `Which items will be restored, one or more units should be separated as comma.
|
|
||||||
wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func runRestoreRepository(c *cli.Context) error {
|
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
setting.NewContext()
|
|
||||||
|
|
||||||
statusCode, errStr := private.RestoreRepo(
|
|
||||||
ctx,
|
|
||||||
c.String("repo_dir"),
|
|
||||||
c.String("owner_name"),
|
|
||||||
c.String("repo_name"),
|
|
||||||
c.StringSlice("units"),
|
|
||||||
)
|
|
||||||
if statusCode == http.StatusOK {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Fatal("Failed to restore repository: %v", errStr)
|
|
||||||
return errors.New(errStr)
|
|
||||||
}
|
|
325
cmd/serv.go
325
cmd/serv.go
|
@ -6,31 +6,26 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"regexp"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/git"
|
|
||||||
"code.gitea.io/gitea/modules/json"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/pprof"
|
|
||||||
"code.gitea.io/gitea/modules/private"
|
"code.gitea.io/gitea/modules/private"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/services/lfs"
|
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/Unknwon/com"
|
||||||
"github.com/kballard/go-shellquote"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
accessDenied = "Repository does not exist or you do not have access"
|
||||||
lfsAuthenticateVerb = "git-lfs-authenticate"
|
lfsAuthenticateVerb = "git-lfs-authenticate"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -41,26 +36,36 @@ var CmdServ = cli.Command{
|
||||||
Description: `Serv provide access auth for repositories`,
|
Description: `Serv provide access auth for repositories`,
|
||||||
Action: runServ,
|
Action: runServ,
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.BoolFlag{
|
cli.StringFlag{
|
||||||
Name: "enable-pprof",
|
Name: "config, c",
|
||||||
},
|
Value: "custom/conf/app.ini",
|
||||||
cli.BoolFlag{
|
Usage: "Custom configuration file path",
|
||||||
Name: "debug",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup(logPath string, debug bool) {
|
func setup(logPath string) error {
|
||||||
_ = 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}`)
|
|
||||||
}
|
|
||||||
setting.NewContext()
|
setting.NewContext()
|
||||||
if debug {
|
log.NewGitLogger(filepath.Join(setting.LogRootPath, logPath))
|
||||||
setting.RunMode = "dev"
|
models.LoadConfigs()
|
||||||
|
|
||||||
|
if setting.UseSQLite3 || setting.UseTiDB {
|
||||||
|
workPath := setting.AppWorkPath
|
||||||
|
if err := os.Chdir(workPath); err != nil {
|
||||||
|
log.GitLogger.Fatal(4, "Failed to change directory %s: %v", workPath, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setting.NewXORMLogService(true)
|
||||||
|
return models.SetEngine()
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseCmd(cmd string) (string, string) {
|
||||||
|
ss := strings.SplitN(cmd, " ", 2)
|
||||||
|
if len(ss) != 2 {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
return ss[0], strings.Replace(ss[1], "'/", "'", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -70,35 +75,31 @@ var (
|
||||||
"git-receive-pack": models.AccessModeWrite,
|
"git-receive-pack": models.AccessModeWrite,
|
||||||
lfsAuthenticateVerb: models.AccessModeNone,
|
lfsAuthenticateVerb: models.AccessModeNone,
|
||||||
}
|
}
|
||||||
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func fail(userMessage, logMessage string, args ...interface{}) error {
|
func fail(userMessage, logMessage string, args ...interface{}) {
|
||||||
// There appears to be a chance to cause a zombie process and failure to read the Exit status
|
|
||||||
// if nothing is outputted on stdout.
|
|
||||||
fmt.Fprintln(os.Stdout, "")
|
|
||||||
fmt.Fprintln(os.Stderr, "Gitea:", userMessage)
|
fmt.Fprintln(os.Stderr, "Gitea:", userMessage)
|
||||||
|
|
||||||
if len(logMessage) > 0 {
|
if len(logMessage) > 0 {
|
||||||
if !setting.IsProd() {
|
if !setting.ProdMode {
|
||||||
fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
|
fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
|
||||||
}
|
}
|
||||||
|
log.GitLogger.Fatal(3, logMessage, args...)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
ctx, cancel := installSignals()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if len(logMessage) > 0 {
|
log.GitLogger.Close()
|
||||||
_ = private.SSHLog(ctx, true, fmt.Sprintf(logMessage+": ", args...))
|
os.Exit(1)
|
||||||
}
|
|
||||||
return cli.NewExitError(fmt.Sprintf("Gitea: %s", userMessage), 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runServ(c *cli.Context) error {
|
func runServ(c *cli.Context) error {
|
||||||
ctx, cancel := installSignals()
|
if c.IsSet("config") {
|
||||||
defer cancel()
|
setting.CustomConf = c.String("config")
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: This needs to internationalised
|
if err := setup("serv.log"); err != nil {
|
||||||
setup("serv.log", c.Bool("debug"))
|
fail("System init failed", fmt.Sprintf("setup: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
if setting.SSH.Disabled {
|
if setting.SSH.Disabled {
|
||||||
println("Gitea: SSH has been disabled")
|
println("Gitea: SSH has been disabled")
|
||||||
|
@ -106,110 +107,76 @@ func runServ(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.Args()) < 1 {
|
if len(c.Args()) < 1 {
|
||||||
if err := cli.ShowSubcommandHelp(c); err != nil {
|
cli.ShowSubcommandHelp(c)
|
||||||
fmt.Printf("error showing subcommand help: %v\n", err)
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
keys := strings.Split(c.Args()[0], "-")
|
|
||||||
if len(keys) != 2 || keys[0] != "key" {
|
|
||||||
return fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
|
|
||||||
}
|
|
||||||
keyID, err := strconv.ParseInt(keys[1], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return fail("Key ID format error", "Invalid key argument: %s", c.Args()[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
|
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
|
||||||
if len(cmd) == 0 {
|
if len(cmd) == 0 {
|
||||||
key, user, err := private.ServNoCommand(ctx, keyID)
|
println("Hi there, You've successfully authenticated, but Gitea does not provide shell access.")
|
||||||
if err != nil {
|
|
||||||
return 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("If this is unexpected, please log in with password and setup Gitea under another user.")
|
println("If this is unexpected, please log in with password and setup Gitea under another user.")
|
||||||
return nil
|
return nil
|
||||||
} else if c.Bool("debug") {
|
|
||||||
log.Debug("SSH_ORIGINAL_COMMAND: %s", os.Getenv("SSH_ORIGINAL_COMMAND"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
words, err := shellquote.Split(cmd)
|
verb, args := parseCmd(cmd)
|
||||||
if err != nil {
|
|
||||||
return fail("Error parsing arguments", "Failed to parse arguments: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(words) < 2 {
|
|
||||||
if git.CheckGitVersionAtLeast("2.29") == nil {
|
|
||||||
// for AGit Flow
|
|
||||||
if cmd == "ssh_info" {
|
|
||||||
fmt.Print(`{"type":"gitea","version":1}`)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fail("Too few arguments", "Too few arguments in cmd: %s", cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
verb := words[0]
|
|
||||||
repoPath := words[1]
|
|
||||||
if repoPath[0] == '/' {
|
|
||||||
repoPath = repoPath[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
var lfsVerb string
|
var lfsVerb string
|
||||||
if verb == lfsAuthenticateVerb {
|
if verb == lfsAuthenticateVerb {
|
||||||
if !setting.LFS.StartServer {
|
if !setting.LFS.StartServer {
|
||||||
return fail("Unknown git command", "LFS authentication request over SSH denied, LFS support is disabled")
|
fail("Unknown git command", "LFS authentication request over SSH denied, LFS support is disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(words) > 2 {
|
argsSplit := strings.Split(args, " ")
|
||||||
lfsVerb = words[2]
|
if len(argsSplit) >= 2 {
|
||||||
|
args = strings.TrimSpace(argsSplit[0])
|
||||||
|
lfsVerb = strings.TrimSpace(argsSplit[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LowerCase and trim the repoPath as that's how they are stored.
|
repoPath := strings.ToLower(strings.Trim(args, "'"))
|
||||||
repoPath = strings.ToLower(strings.TrimSpace(repoPath))
|
|
||||||
|
|
||||||
rr := strings.SplitN(repoPath, "/", 2)
|
rr := strings.SplitN(repoPath, "/", 2)
|
||||||
if len(rr) != 2 {
|
if len(rr) != 2 {
|
||||||
return fail("Invalid repository path", "Invalid repository path: %v", repoPath)
|
fail("Invalid repository path", "Invalid repository path: %v", args)
|
||||||
}
|
}
|
||||||
|
|
||||||
username := strings.ToLower(rr[0])
|
username := strings.ToLower(rr[0])
|
||||||
reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
|
reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
|
||||||
|
|
||||||
if alphaDashDotPattern.MatchString(reponame) {
|
isWiki := false
|
||||||
return fail("Invalid repo name", "Invalid repo name: %s", reponame)
|
unitType := models.UnitTypeCode
|
||||||
|
if strings.HasSuffix(reponame, ".wiki") {
|
||||||
|
isWiki = true
|
||||||
|
unitType = models.UnitTypeWiki
|
||||||
|
reponame = reponame[:len(reponame)-5]
|
||||||
}
|
}
|
||||||
|
|
||||||
if setting.EnablePprof || c.Bool("enable-pprof") {
|
os.Setenv(models.EnvRepoUsername, username)
|
||||||
if err := os.MkdirAll(setting.PprofDataPath, os.ModePerm); err != nil {
|
if isWiki {
|
||||||
return fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err)
|
os.Setenv(models.EnvRepoIsWiki, "true")
|
||||||
}
|
} else {
|
||||||
|
os.Setenv(models.EnvRepoIsWiki, "false")
|
||||||
|
}
|
||||||
|
os.Setenv(models.EnvRepoName, reponame)
|
||||||
|
|
||||||
stopCPUProfiler, err := pprof.DumpCPUProfileForUsername(setting.PprofDataPath, username)
|
repoUser, err := models.GetUserByName(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fail("Internal Server Error", "Unable to start CPU profile: %v", err)
|
if models.IsErrUserNotExist(err) {
|
||||||
|
fail("Repository owner does not exist", "Unregistered owner: %s", username)
|
||||||
}
|
}
|
||||||
defer func() {
|
fail("Internal error", "Failed to get repository owner (%s): %v", username, err)
|
||||||
stopCPUProfiler()
|
}
|
||||||
err := pprof.DumpMemProfileForUsername(setting.PprofDataPath, username)
|
|
||||||
if err != nil {
|
repo, err := models.GetRepositoryByName(repoUser.ID, reponame)
|
||||||
_ = fail("Internal Server Error", "Unable to dump Mem Profile: %v", err)
|
if err != nil {
|
||||||
}
|
if models.IsErrRepoNotExist(err) {
|
||||||
}()
|
fail(accessDenied, "Repository does not exist: %s/%s", repoUser.Name, reponame)
|
||||||
|
}
|
||||||
|
fail("Internal error", "Failed to get repository: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
requestedMode, has := allowedCommands[verb]
|
requestedMode, has := allowedCommands[verb]
|
||||||
if !has {
|
if !has {
|
||||||
return fail("Unknown git command", "Unknown git command %s", verb)
|
fail("Unknown git command", "Unknown git command %s", verb)
|
||||||
}
|
}
|
||||||
|
|
||||||
if verb == lfsAuthenticateVerb {
|
if verb == lfsAuthenticateVerb {
|
||||||
|
@ -218,53 +185,98 @@ func runServ(c *cli.Context) error {
|
||||||
} else if lfsVerb == "download" {
|
} else if lfsVerb == "download" {
|
||||||
requestedMode = models.AccessModeRead
|
requestedMode = models.AccessModeRead
|
||||||
} else {
|
} else {
|
||||||
return fail("Unknown LFS verb", "Unknown lfs verb %s", lfsVerb)
|
fail("Unknown LFS verb", "Unknown lfs verb %s", lfsVerb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
results, err := private.ServCommand(ctx, keyID, username, reponame, requestedMode, verb, lfsVerb)
|
// Prohibit push to mirror repositories.
|
||||||
if err != nil {
|
if requestedMode > models.AccessModeRead && repo.IsMirror {
|
||||||
if private.IsErrServCommand(err) {
|
fail("mirror repository is read-only", "")
|
||||||
errServCommand := err.(private.ErrServCommand)
|
}
|
||||||
if errServCommand.StatusCode != http.StatusInternalServerError {
|
|
||||||
return fail("Unauthorized", "%s", errServCommand.Error())
|
// Allow anonymous clone for public repositories.
|
||||||
}
|
var (
|
||||||
return fail("Internal Server Error", "%s", errServCommand.Error())
|
keyID int64
|
||||||
}
|
user *models.User
|
||||||
return fail("Internal Server Error", "%s", err.Error())
|
)
|
||||||
|
if requestedMode == models.AccessModeWrite || repo.IsPrivate {
|
||||||
|
keys := strings.Split(c.Args()[0], "-")
|
||||||
|
if len(keys) != 2 {
|
||||||
|
fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := models.GetPublicKeyByID(com.StrTo(keys[1]).MustInt64())
|
||||||
|
if err != nil {
|
||||||
|
fail("Invalid key ID", "Invalid key ID[%s]: %v", c.Args()[0], err)
|
||||||
|
}
|
||||||
|
keyID = key.ID
|
||||||
|
|
||||||
|
// Check deploy key or user key.
|
||||||
|
if key.Type == models.KeyTypeDeploy {
|
||||||
|
if key.Mode < requestedMode {
|
||||||
|
fail("Key permission denied", "Cannot push with deployment key: %d", key.ID)
|
||||||
|
}
|
||||||
|
// Check if this deploy key belongs to current repository.
|
||||||
|
if !models.HasDeployKey(key.ID, repo.ID) {
|
||||||
|
fail("Key access denied", "Deploy key access denied: [key_id: %d, repo_id: %d]", key.ID, repo.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update deploy key activity.
|
||||||
|
deployKey, err := models.GetDeployKeyByRepo(key.ID, repo.ID)
|
||||||
|
if err != nil {
|
||||||
|
fail("Internal error", "GetDeployKey: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
deployKey.Updated = time.Now()
|
||||||
|
if err = models.UpdateDeployKey(deployKey); err != nil {
|
||||||
|
fail("Internal error", "UpdateDeployKey: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
user, err = models.GetUserByKeyID(key.ID)
|
||||||
|
if err != nil {
|
||||||
|
fail("internal error", "Failed to get user by key ID(%d): %v", keyID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mode, err := models.AccessLevel(user.ID, repo)
|
||||||
|
if err != nil {
|
||||||
|
fail("Internal error", "Failed to check access: %v", err)
|
||||||
|
} else if mode < requestedMode {
|
||||||
|
clientMessage := accessDenied
|
||||||
|
if mode >= models.AccessModeRead {
|
||||||
|
clientMessage = "You do not have sufficient authorization for this action"
|
||||||
|
}
|
||||||
|
fail(clientMessage,
|
||||||
|
"User %s does not have level %v access to repository %s",
|
||||||
|
user.Name, requestedMode, repoPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !repo.CheckUnitUser(user.ID, user.IsAdmin, unitType) {
|
||||||
|
fail("You do not have allowed for this action",
|
||||||
|
"User %s does not have allowed access to repository %s 's code",
|
||||||
|
user.Name, 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
|
//LFS token authentication
|
||||||
if verb == lfsAuthenticateVerb {
|
if verb == lfsAuthenticateVerb {
|
||||||
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
|
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, repoUser.Name, repo.Name)
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
claims := lfs.Claims{
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||||
StandardClaims: jwt.StandardClaims{
|
"repo": repo.ID,
|
||||||
ExpiresAt: now.Add(setting.LFS.HTTPAuthExpiry).Unix(),
|
"op": lfsVerb,
|
||||||
NotBefore: now.Unix(),
|
"exp": now.Add(5 * time.Minute).Unix(),
|
||||||
},
|
"nbf": now.Unix(),
|
||||||
RepoID: results.RepoID,
|
})
|
||||||
Op: lfsVerb,
|
|
||||||
UserID: results.UserID,
|
|
||||||
}
|
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
||||||
|
|
||||||
// Sign and get the complete encoded token as a string using the secret
|
// Sign and get the complete encoded token as a string using the secret
|
||||||
tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes)
|
tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fail("Internal error", "Failed to sign JWT token: %v", err)
|
fail("Internal error", "Failed to sign JWT token: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenAuthentication := &models.LFSTokenResponse{
|
tokenAuthentication := &models.LFSTokenResponse{
|
||||||
|
@ -276,8 +288,9 @@ func runServ(c *cli.Context) error {
|
||||||
enc := json.NewEncoder(os.Stdout)
|
enc := json.NewEncoder(os.Stdout)
|
||||||
err = enc.Encode(tokenAuthentication)
|
err = enc.Encode(tokenAuthentication)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fail("Internal error", "Failed to encode LFS json response: %v", err)
|
fail("Internal error", "Failed to encode LFS json response: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,23 +302,31 @@ func runServ(c *cli.Context) error {
|
||||||
var gitcmd *exec.Cmd
|
var gitcmd *exec.Cmd
|
||||||
verbs := strings.Split(verb, " ")
|
verbs := strings.Split(verb, " ")
|
||||||
if len(verbs) == 2 {
|
if len(verbs) == 2 {
|
||||||
gitcmd = exec.CommandContext(ctx, verbs[0], verbs[1], repoPath)
|
gitcmd = exec.Command(verbs[0], verbs[1], repoPath)
|
||||||
} else {
|
} else {
|
||||||
gitcmd = exec.CommandContext(ctx, verb, repoPath)
|
gitcmd = exec.Command(verb, repoPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isWiki {
|
||||||
|
if err = repo.InitWiki(); 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.Dir = setting.RepoRootPath
|
||||||
gitcmd.Stdout = os.Stdout
|
gitcmd.Stdout = os.Stdout
|
||||||
gitcmd.Stdin = os.Stdin
|
gitcmd.Stdin = os.Stdin
|
||||||
gitcmd.Stderr = os.Stderr
|
gitcmd.Stderr = os.Stderr
|
||||||
if err = gitcmd.Run(); err != nil {
|
if err = gitcmd.Run(); err != nil {
|
||||||
return fail("Internal error", "Failed to execute git command: %v", err)
|
fail("Internal error", "Failed to execute git command: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update user key activity.
|
// Update user key activity.
|
||||||
if results.KeyID > 0 {
|
if keyID > 0 {
|
||||||
if err = private.UpdatePublicKeyInRepo(ctx, results.KeyID, results.RepoID); err != nil {
|
if err = private.UpdatePublicKeyUpdated(keyID); err != nil {
|
||||||
return fail("Internal error", "UpdatePublicKeyInRepo: %v", err)
|
fail("Internal error", "UpdatePublicKey: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
242
cmd/web.go
242
cmd/web.go
|
@ -5,20 +5,21 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/fcgi"
|
||||||
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
|
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/graceful"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/markup/external"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/routers"
|
"code.gitea.io/gitea/routers"
|
||||||
"code.gitea.io/gitea/routers/install"
|
"code.gitea.io/gitea/routers/routes"
|
||||||
|
|
||||||
|
"github.com/Unknwon/com"
|
||||||
context2 "github.com/gorilla/context"
|
context2 "github.com/gorilla/context"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
ini "gopkg.in/ini.v1"
|
ini "gopkg.in/ini.v1"
|
||||||
|
@ -38,160 +39,73 @@ and it takes care of all the other things for you`,
|
||||||
Usage: "Temporary port number to prevent conflict",
|
Usage: "Temporary port number to prevent conflict",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "install-port",
|
Name: "config, c",
|
||||||
Value: "3000",
|
Value: "custom/conf/app.ini",
|
||||||
Usage: "Temporary port number to run the install page on to prevent conflict",
|
Usage: "Custom configuration file path",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "pid, P",
|
Name: "pid, P",
|
||||||
Value: setting.PIDFile,
|
Value: "/var/run/gitea.pid",
|
||||||
Usage: "Custom pid file path",
|
Usage: "Custom pid file path",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "quiet, q",
|
|
||||||
Usage: "Only display Fatal logging errors until logging is set-up",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "verbose",
|
|
||||||
Usage: "Set initial logging to TRACE level until logging is properly set-up",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func runHTTPRedirector() {
|
|
||||||
source := fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.PortToRedirect)
|
|
||||||
dest := strings.TrimSuffix(setting.AppURL, "/")
|
|
||||||
log.Info("Redirecting: %s to %s", source, dest)
|
|
||||||
|
|
||||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
target := dest + r.URL.Path
|
|
||||||
if len(r.URL.RawQuery) > 0 {
|
|
||||||
target += "?" + r.URL.RawQuery
|
|
||||||
}
|
|
||||||
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
|
||||||
})
|
|
||||||
|
|
||||||
var err = runHTTP("tcp", source, "HTTP Redirector", context2.ClearHandler(handler))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Failed to start port redirection: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runWeb(ctx *cli.Context) error {
|
func runWeb(ctx *cli.Context) error {
|
||||||
if ctx.Bool("verbose") {
|
if ctx.IsSet("config") {
|
||||||
_ = log.DelLogger("console")
|
setting.CustomConf = ctx.String("config")
|
||||||
log.NewLogger(0, "console", "console", fmt.Sprintf(`{"level": "trace", "colorize": %t, "stacktraceLevel": "none"}`, log.CanColorStdout))
|
|
||||||
} else if ctx.Bool("quiet") {
|
|
||||||
_ = log.DelLogger("console")
|
|
||||||
log.NewLogger(0, "console", "console", fmt.Sprintf(`{"level": "fatal", "colorize": %t, "stacktraceLevel": "none"}`, log.CanColorStdout))
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if panicked := recover(); panicked != nil {
|
|
||||||
log.Fatal("PANIC: %v\n%s", panicked, string(log.Stack(2)))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set pid file setting
|
|
||||||
if ctx.IsSet("pid") {
|
if ctx.IsSet("pid") {
|
||||||
setting.PIDFile = ctx.String("pid")
|
setting.CustomPID = ctx.String("pid")
|
||||||
setting.WritePIDFile = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform pre-initialization
|
routers.GlobalInit()
|
||||||
needsInstall := install.PreloadSettings(graceful.GetManager().HammerContext())
|
|
||||||
if needsInstall {
|
|
||||||
// Flag for port number in case first time run conflict
|
|
||||||
if ctx.IsSet("port") {
|
|
||||||
if err := setPort(ctx.String("port")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ctx.IsSet("install-port") {
|
|
||||||
if err := setPort(ctx.String("install-port")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c := install.Routes()
|
|
||||||
err := listen(c, false)
|
|
||||||
select {
|
|
||||||
case <-graceful.GetManager().IsShutdown():
|
|
||||||
<-graceful.GetManager().Done()
|
|
||||||
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
|
||||||
log.Close()
|
|
||||||
return err
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
NoInstallListener()
|
|
||||||
}
|
|
||||||
|
|
||||||
if setting.EnablePprof {
|
external.RegisterParsers()
|
||||||
go func() {
|
|
||||||
log.Info("Starting pprof server on localhost:6060")
|
|
||||||
log.Info("%v", http.ListenAndServe("localhost:6060", nil))
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("Global init")
|
m := routes.NewMacaron()
|
||||||
// Perform global initialization
|
routes.RegisterRoutes(m)
|
||||||
routers.GlobalInit(graceful.GetManager().HammerContext())
|
|
||||||
|
|
||||||
// Override the provided port number within the configuration
|
// Flag for port number in case first time run conflict.
|
||||||
if ctx.IsSet("port") {
|
if ctx.IsSet("port") {
|
||||||
if err := setPort(ctx.String("port")); err != nil {
|
setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, ctx.String("port"), 1)
|
||||||
return err
|
setting.HTTPPort = ctx.String("port")
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up Chi routes
|
switch setting.Protocol {
|
||||||
c := routers.NormalRoutes()
|
case setting.UnixSocket:
|
||||||
err := listen(c, true)
|
case setting.FCGI:
|
||||||
<-graceful.GetManager().Done()
|
default:
|
||||||
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
// Save LOCAL_ROOT_URL if port changed
|
||||||
log.Close()
|
cfg := ini.Empty()
|
||||||
return err
|
if com.IsFile(setting.CustomConf) {
|
||||||
}
|
// Keeps custom settings if there is already something.
|
||||||
|
if err := cfg.Append(setting.CustomConf); err != nil {
|
||||||
|
return fmt.Errorf("Failed to load custom conf '%s': %v", setting.CustomConf, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func setPort(port string) error {
|
defaultLocalURL := string(setting.Protocol) + "://"
|
||||||
setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, port, 1)
|
if setting.HTTPAddr == "0.0.0.0" {
|
||||||
setting.HTTPPort = port
|
defaultLocalURL += "localhost"
|
||||||
|
} else {
|
||||||
|
defaultLocalURL += setting.HTTPAddr
|
||||||
|
}
|
||||||
|
defaultLocalURL += ":" + setting.HTTPPort + "/"
|
||||||
|
|
||||||
switch setting.Protocol {
|
|
||||||
case setting.UnixSocket:
|
|
||||||
case setting.FCGI:
|
|
||||||
case setting.FCGIUnix:
|
|
||||||
default:
|
|
||||||
defaultLocalURL := string(setting.Protocol) + "://"
|
|
||||||
if setting.HTTPAddr == "0.0.0.0" {
|
|
||||||
defaultLocalURL += "localhost"
|
|
||||||
} else {
|
|
||||||
defaultLocalURL += setting.HTTPAddr
|
|
||||||
}
|
|
||||||
defaultLocalURL += ":" + setting.HTTPPort + "/"
|
|
||||||
|
|
||||||
// Save LOCAL_ROOT_URL if port changed
|
|
||||||
setting.CreateOrAppendToCustomConf(func(cfg *ini.File) {
|
|
||||||
cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
|
cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
|
||||||
})
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func listen(m http.Handler, handleRedirector bool) error {
|
if err := cfg.SaveTo(setting.CustomConf); err != nil {
|
||||||
listenAddr := setting.HTTPAddr
|
return fmt.Errorf("Error saving generated JWT Secret to custom config: %v", err)
|
||||||
if setting.Protocol != setting.UnixSocket && setting.Protocol != setting.FCGIUnix {
|
}
|
||||||
listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var listenAddr string
|
||||||
|
if setting.Protocol == setting.UnixSocket {
|
||||||
|
listenAddr = fmt.Sprintf("%s", setting.HTTPAddr)
|
||||||
|
} else {
|
||||||
|
listenAddr = fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.HTTPPort)
|
||||||
}
|
}
|
||||||
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
|
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
|
||||||
|
|
||||||
|
@ -199,48 +113,48 @@ func listen(m http.Handler, handleRedirector bool) error {
|
||||||
log.Info("LFS server enabled")
|
log.Info("LFS server enabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if setting.EnablePprof {
|
||||||
|
go func() {
|
||||||
|
log.Info("%v", http.ListenAndServe("localhost:6060", nil))
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
switch setting.Protocol {
|
switch setting.Protocol {
|
||||||
case setting.HTTP:
|
case setting.HTTP:
|
||||||
if handleRedirector {
|
err = runHTTP(listenAddr, context2.ClearHandler(m))
|
||||||
NoHTTPRedirector()
|
|
||||||
}
|
|
||||||
err = runHTTP("tcp", listenAddr, "Web", context2.ClearHandler(m))
|
|
||||||
case setting.HTTPS:
|
case setting.HTTPS:
|
||||||
if setting.EnableLetsEncrypt {
|
err = runHTTPS(listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
|
||||||
err = runLetsEncrypt(listenAddr, setting.Domain, setting.LetsEncryptDirectory, setting.LetsEncryptEmail, context2.ClearHandler(m))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if handleRedirector {
|
|
||||||
if setting.RedirectOtherPort {
|
|
||||||
go runHTTPRedirector()
|
|
||||||
} else {
|
|
||||||
NoHTTPRedirector()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
|
|
||||||
case setting.FCGI:
|
case setting.FCGI:
|
||||||
if handleRedirector {
|
listener, err := net.Listen("tcp", listenAddr)
|
||||||
NoHTTPRedirector()
|
if err != nil {
|
||||||
|
log.Fatal(4, "Failed to bind %s", listenAddr, err)
|
||||||
}
|
}
|
||||||
err = runFCGI("tcp", listenAddr, "FCGI Web", context2.ClearHandler(m))
|
defer listener.Close()
|
||||||
|
err = fcgi.Serve(listener, context2.ClearHandler(m))
|
||||||
case setting.UnixSocket:
|
case setting.UnixSocket:
|
||||||
if handleRedirector {
|
if err := os.Remove(listenAddr); err != nil && !os.IsNotExist(err) {
|
||||||
NoHTTPRedirector()
|
log.Fatal(4, "Failed to remove unix socket directory %s: %v", listenAddr, err)
|
||||||
}
|
}
|
||||||
err = runHTTP("unix", listenAddr, "Web", context2.ClearHandler(m))
|
var listener *net.UnixListener
|
||||||
case setting.FCGIUnix:
|
listener, err = net.ListenUnix("unix", &net.UnixAddr{Name: listenAddr, Net: "unix"})
|
||||||
if handleRedirector {
|
if err != nil {
|
||||||
NoHTTPRedirector()
|
break // Handle error after switch
|
||||||
}
|
}
|
||||||
err = runFCGI("unix", listenAddr, "Web", context2.ClearHandler(m))
|
|
||||||
|
// 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:
|
default:
|
||||||
log.Fatal("Invalid protocol: %s", setting.Protocol)
|
log.Fatal(4, "Invalid protocol: %s", setting.Protocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
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)
|
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build !windows
|
||||||
|
|
||||||
// Copyright 2016 The Gitea Authors. All rights reserved.
|
// Copyright 2016 The Gitea Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
@ -6,60 +8,38 @@ package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/fcgi"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/graceful"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
|
"github.com/facebookgo/grace/gracehttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func runHTTP(network, listenAddr, name string, m http.Handler) error {
|
func runHTTP(listenAddr string, m http.Handler) error {
|
||||||
return graceful.HTTPListenAndServe(network, listenAddr, name, m)
|
return gracehttp.Serve(&http.Server{
|
||||||
}
|
Addr: listenAddr,
|
||||||
|
Handler: m,
|
||||||
func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler) error {
|
})
|
||||||
return graceful.HTTPListenAndServeTLS(network, listenAddr, name, certFile, keyFile, m)
|
}
|
||||||
}
|
|
||||||
|
func runHTTPS(listenAddr, certFile, keyFile string, m http.Handler) error {
|
||||||
func runHTTPSWithTLSConfig(network, listenAddr, name string, tlsConfig *tls.Config, m http.Handler) error {
|
config := &tls.Config{
|
||||||
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m)
|
MinVersion: tls.VersionTLS10,
|
||||||
}
|
}
|
||||||
|
if config.NextProtos == nil {
|
||||||
// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
|
config.NextProtos = []string{"http/1.1"}
|
||||||
func NoHTTPRedirector() {
|
}
|
||||||
graceful.GetManager().InformCleanup()
|
|
||||||
}
|
config.Certificates = make([]tls.Certificate, 1)
|
||||||
|
var err error
|
||||||
// NoMainListener tells our cleanup routine that we will not be using a possibly provided listener
|
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||||
// for our main HTTP/HTTPS service
|
if err != nil {
|
||||||
func NoMainListener() {
|
log.Fatal(4, "Failed to load https cert file %s: %v", listenAddr, err)
|
||||||
graceful.GetManager().InformCleanup()
|
}
|
||||||
}
|
|
||||||
|
return gracehttp.Serve(&http.Server{
|
||||||
// NoInstallListener tells our cleanup routine that we will not be using a possibly provided listener
|
Addr: listenAddr,
|
||||||
// for our install HTTP/HTTPS service
|
Handler: m,
|
||||||
func NoInstallListener() {
|
TLSConfig: config,
|
||||||
graceful.GetManager().InformCleanup()
|
|
||||||
}
|
|
||||||
|
|
||||||
func runFCGI(network, listenAddr, name string, m http.Handler) error {
|
|
||||||
// This needs to handle stdin as fcgi point
|
|
||||||
fcgiServer := graceful.NewServer(network, listenAddr, name)
|
|
||||||
|
|
||||||
err := fcgiServer.ListenAndServe(func(listener net.Listener) error {
|
|
||||||
return fcgi.Serve(listener, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
|
||||||
if setting.AppSubURL != "" {
|
|
||||||
req.URL.Path = strings.TrimPrefix(req.URL.Path, setting.AppSubURL)
|
|
||||||
}
|
|
||||||
m.ServeHTTP(resp, req)
|
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Failed to start FCGI main server: %v", err)
|
|
||||||
}
|
|
||||||
log.Info("FCGI Listener: %s Closed", listenAddr)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
|
|
||||||
"github.com/caddyserver/certmagic"
|
|
||||||
context2 "github.com/gorilla/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler) error {
|
|
||||||
|
|
||||||
// If HTTP Challenge enabled, needs to be serving on port 80. For TLSALPN needs 443.
|
|
||||||
// Due to docker port mapping this can't be checked programmatically
|
|
||||||
// TODO: these are placeholders until we add options for each in settings with appropriate warning
|
|
||||||
enableHTTPChallenge := true
|
|
||||||
enableTLSALPNChallenge := true
|
|
||||||
altHTTPPort := 0
|
|
||||||
altTLSALPNPort := 0
|
|
||||||
|
|
||||||
if p, err := strconv.Atoi(setting.PortToRedirect); err == nil {
|
|
||||||
altHTTPPort = p
|
|
||||||
}
|
|
||||||
if p, err := strconv.Atoi(setting.HTTPPort); err == nil {
|
|
||||||
altTLSALPNPort = p
|
|
||||||
}
|
|
||||||
|
|
||||||
magic := certmagic.NewDefault()
|
|
||||||
magic.Storage = &certmagic.FileStorage{Path: directory}
|
|
||||||
myACME := certmagic.NewACMEManager(magic, certmagic.ACMEManager{
|
|
||||||
Email: email,
|
|
||||||
Agreed: setting.LetsEncryptTOS,
|
|
||||||
DisableHTTPChallenge: !enableHTTPChallenge,
|
|
||||||
DisableTLSALPNChallenge: !enableTLSALPNChallenge,
|
|
||||||
ListenHost: setting.HTTPAddr,
|
|
||||||
AltTLSALPNPort: altTLSALPNPort,
|
|
||||||
AltHTTPPort: altHTTPPort,
|
|
||||||
})
|
|
||||||
|
|
||||||
magic.Issuers = []certmagic.Issuer{myACME}
|
|
||||||
|
|
||||||
// this obtains certificates or renews them if necessary
|
|
||||||
err := magic.ManageSync([]string{domain})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsConfig := magic.TLSConfig()
|
|
||||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
|
|
||||||
|
|
||||||
if enableHTTPChallenge {
|
|
||||||
go func() {
|
|
||||||
log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
|
|
||||||
// all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
|
|
||||||
var err = runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
return runHTTPSWithTLSConfig("tcp", listenAddr, "Web", tlsConfig, context2.ClearHandler(m))
|
|
||||||
}
|
|
||||||
|
|
||||||
func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if r.Method != "GET" && r.Method != "HEAD" {
|
|
||||||
http.Error(w, "Use HTTPS", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Remove the trailing slash at the end of setting.AppURL, the request
|
|
||||||
// URI always contains a leading slash, which would result in a double
|
|
||||||
// slash
|
|
||||||
target := strings.TrimSuffix(setting.AppURL, "/") + r.URL.RequestURI()
|
|
||||||
http.Redirect(w, r, target, http.StatusFound)
|
|
||||||
}
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -0,0 +1,584 @@
|
||||||
|
; App name that shows on every page title
|
||||||
|
APP_NAME = Gitea: Git with a cup of tea
|
||||||
|
; Change it if you run locally
|
||||||
|
RUN_USER = git
|
||||||
|
; Either "dev", "prod" or "test", default is "dev"
|
||||||
|
RUN_MODE = dev
|
||||||
|
|
||||||
|
[repository]
|
||||||
|
ROOT =
|
||||||
|
SCRIPT_TYPE = bash
|
||||||
|
; Default ANSI charset
|
||||||
|
ANSI_CHARSET =
|
||||||
|
; Force every new repository to be private
|
||||||
|
FORCE_PRIVATE = false
|
||||||
|
; Global maximum creation limit of repository per user, -1 means no limit
|
||||||
|
MAX_CREATION_LIMIT = -1
|
||||||
|
; Mirror sync queue length, increase if mirror syncing starts hanging
|
||||||
|
MIRROR_QUEUE_LENGTH = 1000
|
||||||
|
; Patch test queue length, increase if pull request patch testing starts hanging
|
||||||
|
PULL_REQUEST_QUEUE_LENGTH = 1000
|
||||||
|
; Preferred Licenses to place at the top of the List
|
||||||
|
; Name must match file name in conf/license or custom/conf/license
|
||||||
|
PREFERRED_LICENSES = Apache License 2.0,MIT License
|
||||||
|
; Disable ability to interact with repositories by HTTP protocol
|
||||||
|
DISABLE_HTTP_GIT = false
|
||||||
|
; Force ssh:// clone url instead of scp-style uri when default SSH port is used
|
||||||
|
USE_COMPAT_SSH_URI = false
|
||||||
|
|
||||||
|
[repository.editor]
|
||||||
|
; List of file extensions that should have line wraps in the CodeMirror editor
|
||||||
|
; Separate extensions with a comma. To line wrap files w/o extension, just put a comma
|
||||||
|
LINE_WRAP_EXTENSIONS = .txt,.md,.markdown,.mdown,.mkd,
|
||||||
|
; Valid file modes that have a preview API associated with them, such as api/v1/markdown
|
||||||
|
; Separate values by commas. Preview tab in edit mode won't show if the file extension doesn't match
|
||||||
|
PREVIEWABLE_FILE_MODES = markdown
|
||||||
|
|
||||||
|
[repository.local]
|
||||||
|
; Path for uploads. Defaults to `tmp/local-repo`
|
||||||
|
LOCAL_COPY_PATH = tmp/local-repo
|
||||||
|
|
||||||
|
[repository.upload]
|
||||||
|
; Whether repository file uploads are enabled. Defaults to `true`
|
||||||
|
ENABLED = true
|
||||||
|
; Path for uploads. Defaults to `data/tmp/uploads` (tmp gets deleted on gitea restart)
|
||||||
|
TEMP_PATH = data/tmp/uploads
|
||||||
|
; One or more allowed types, e.g. image/jpeg|image/png. Nothing means any file type
|
||||||
|
ALLOWED_TYPES =
|
||||||
|
; Max size of each file in MB. Defaults to 3MB
|
||||||
|
FILE_MAX_SIZE = 3
|
||||||
|
; Max number of files per upload. Defaults to 5
|
||||||
|
MAX_FILES = 5
|
||||||
|
|
||||||
|
[ui]
|
||||||
|
; Number of repositories that are showed in one explore page
|
||||||
|
EXPLORE_PAGING_NUM = 20
|
||||||
|
; Number of issues that are showed in one page
|
||||||
|
ISSUE_PAGING_NUM = 10
|
||||||
|
; Number of maximum commits showed in one activity feed
|
||||||
|
FEED_MAX_COMMIT_NUM = 5
|
||||||
|
; Value of `theme-color` meta tag, used by Android >= 5.0
|
||||||
|
; An invalid color like "none" or "disable" will have the default style
|
||||||
|
; More info: https://developers.google.com/web/updates/2014/11/Support-for-theme-color-in-Chrome-39-for-Android
|
||||||
|
THEME_COLOR_META_TAG = `#6cc644`
|
||||||
|
; Max size of files to be displayed (defaults is 8MiB)
|
||||||
|
MAX_DISPLAY_FILE_SIZE = 8388608
|
||||||
|
; Whether show the user email in the Explore Users page
|
||||||
|
SHOW_USER_EMAIL = true
|
||||||
|
|
||||||
|
[ui.admin]
|
||||||
|
; Number of users that are showed in one page
|
||||||
|
USER_PAGING_NUM = 50
|
||||||
|
; Number of repos that are showed in one page
|
||||||
|
REPO_PAGING_NUM = 50
|
||||||
|
; Number of notices that are showed in one page
|
||||||
|
NOTICE_PAGING_NUM = 25
|
||||||
|
; Number of organization that are showed in one page
|
||||||
|
ORG_PAGING_NUM = 50
|
||||||
|
|
||||||
|
[ui.user]
|
||||||
|
; Number of repos that are showed in one page
|
||||||
|
REPO_PAGING_NUM = 15
|
||||||
|
|
||||||
|
[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 extensions with a comma. To render files w/o extension as markdown, just put a comma
|
||||||
|
FILE_EXTENSIONS = .md,.markdown,.mdown,.mkd
|
||||||
|
|
||||||
|
[server]
|
||||||
|
; Listen protocol. One of 'http', 'https', 'unix' or 'fcgi'.
|
||||||
|
PROTOCOL = http
|
||||||
|
DOMAIN = localhost
|
||||||
|
ROOT_URL = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
|
||||||
|
; Listen address. Either a IPv4/IPv6 address or the path to a unix socket.
|
||||||
|
HTTP_ADDR = 0.0.0.0
|
||||||
|
HTTP_PORT = 3000
|
||||||
|
; Permission for unix socket
|
||||||
|
UNIX_SOCKET_PERMISSION = 666
|
||||||
|
; Local (DMZ) URL for Gitea workers (such as SSH update) accessing web service.
|
||||||
|
; In most cases you do not need to change the default value.
|
||||||
|
; Alter it only if your SSH server node is not the same as HTTP node.
|
||||||
|
LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
|
||||||
|
; Disable SSH feature when not available
|
||||||
|
DISABLE_SSH = false
|
||||||
|
; Whether use builtin SSH server or not.
|
||||||
|
START_SSH_SERVER = false
|
||||||
|
; Username to use for 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
|
||||||
|
; Network interface builtin SSH server listens on
|
||||||
|
SSH_LISTEN_HOST =
|
||||||
|
; Port number to be exposed in clone URL
|
||||||
|
SSH_PORT = 22
|
||||||
|
; Port number builtin SSH server listens on
|
||||||
|
SSH_LISTEN_PORT = %(SSH_PORT)s
|
||||||
|
; Root path of SSH directory, default is '~/.ssh', but you have to use '/home/git/.ssh'.
|
||||||
|
SSH_ROOT_PATH =
|
||||||
|
; For built-in SSH server only, 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 built-in SSH server only, 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 built-in SSH server only, 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 when test public key using ssh-keygen,
|
||||||
|
; default is system temporary directory.
|
||||||
|
SSH_KEY_TEST_PATH =
|
||||||
|
; Path to ssh-keygen, default is 'ssh-keygen' and let shell find out which one to call.
|
||||||
|
SSH_KEYGEN_PATH = ssh-keygen
|
||||||
|
; 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
|
||||||
|
; Upper level of template and static file path
|
||||||
|
; default is the path where Gitea is executed
|
||||||
|
STATIC_ROOT_PATH =
|
||||||
|
; Default path for App data
|
||||||
|
APP_DATA_PATH = data
|
||||||
|
; Application level GZIP support
|
||||||
|
ENABLE_GZIP = false
|
||||||
|
; Landing page for non-logged users, can be "home" or "explore"
|
||||||
|
LANDING_PAGE = home
|
||||||
|
; Enables git-lfs support. true or false, default is false.
|
||||||
|
LFS_START_SERVER = false
|
||||||
|
; Where your lfs files put on, default is data/lfs.
|
||||||
|
LFS_CONTENT_PATH = data/lfs
|
||||||
|
; LFS authentication secret, changed this to yourself.
|
||||||
|
LFS_JWT_SECRET =
|
||||||
|
|
||||||
|
; 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
|
||||||
|
PASSWD =
|
||||||
|
; For "postgres" only, either "disable", "require" or "verify-full"
|
||||||
|
SSL_MODE = disable
|
||||||
|
; For "sqlite3" and "tidb", use absolute path when you start as service
|
||||||
|
PATH = data/gitea.db
|
||||||
|
; For "sqlite3" only. Query timeout
|
||||||
|
SQLITE_TIMEOUT = 500
|
||||||
|
; For iterate buffer, default is 50
|
||||||
|
ITERATE_BUFFER_SIZE = 50
|
||||||
|
|
||||||
|
[indexer]
|
||||||
|
ISSUE_INDEXER_PATH = indexers/issues.bleve
|
||||||
|
; 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]
|
||||||
|
; Disable regular (non-admin) users to create organizations
|
||||||
|
DISABLE_REGULAR_ORG_CREATION = false
|
||||||
|
|
||||||
|
[security]
|
||||||
|
; Whether the installer is disabled
|
||||||
|
INSTALL_LOCK = false
|
||||||
|
; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!!
|
||||||
|
SECRET_KEY = !#@FDEWREWR&*(
|
||||||
|
; Auto-login remember days
|
||||||
|
LOGIN_REMEMBER_DAYS = 7
|
||||||
|
COOKIE_USERNAME = gitea_awesome
|
||||||
|
COOKIE_REMEMBER_NAME = gitea_incredible
|
||||||
|
; Reverse proxy authentication header name of user name
|
||||||
|
REVERSE_PROXY_AUTHENTICATION_USER = X-WEBAUTH-USER
|
||||||
|
; Sets the minimum password length for new Users
|
||||||
|
MIN_PASSWORD_LENGTH = 6
|
||||||
|
; True when users are allowed to import local server paths
|
||||||
|
IMPORT_LOCAL_PATHS = false
|
||||||
|
; 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 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 confirm forgot password reset process
|
||||||
|
RESET_PASSWD_CODE_LIVE_MINUTES = 180
|
||||||
|
; User need to confirm e-mail for registration
|
||||||
|
REGISTER_EMAIL_CONFIRM = false
|
||||||
|
; Does not allow register and admin create account only
|
||||||
|
DISABLE_REGISTRATION = false
|
||||||
|
; User must sign in to view anything.
|
||||||
|
REQUIRE_SIGNIN_VIEW = false
|
||||||
|
; Mail notification
|
||||||
|
ENABLE_NOTIFY_MAIL = false
|
||||||
|
; More detail: https://github.com/go-gitea/gitea/issues/165
|
||||||
|
ENABLE_REVERSE_PROXY_AUTHENTICATION = false
|
||||||
|
ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false
|
||||||
|
; Enable captcha validation for registration
|
||||||
|
ENABLE_CAPTCHA = true
|
||||||
|
; Default value for KeepEmailPrivate
|
||||||
|
; New user will get the value of this setting copied into their profile
|
||||||
|
DEFAULT_KEEP_EMAIL_PRIVATE = false
|
||||||
|
; Default value for AllowCreateOrganization
|
||||||
|
; New user will have rights set to create organizations depending on this setting
|
||||||
|
DEFAULT_ALLOW_CREATE_ORGANIZATION = 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 could 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 true. The user's email replaced with a
|
||||||
|
; concatenation of the user name in lower case, "@" and NO_REPLY_ADDRESS.
|
||||||
|
NO_REPLY_ADDRESS = noreply.example.org
|
||||||
|
|
||||||
|
[webhook]
|
||||||
|
; Hook task queue length, increase if webhook shooting starts hanging
|
||||||
|
QUEUE_LENGTH = 1000
|
||||||
|
; Deliver timeout in seconds
|
||||||
|
DELIVER_TIMEOUT = 5
|
||||||
|
; Allow insecure certification
|
||||||
|
SKIP_TLS_VERIFY = false
|
||||||
|
; Number of history information in each page
|
||||||
|
PAGING_NUM = 10
|
||||||
|
|
||||||
|
[mailer]
|
||||||
|
ENABLED = false
|
||||||
|
; Buffer length of channel, keep it as it is if you don't know what it is.
|
||||||
|
SEND_BUFFER_LEN = 100
|
||||||
|
; Name displayed in mail title
|
||||||
|
SUBJECT = %(APP_NAME)s
|
||||||
|
; Mail server
|
||||||
|
; Gmail: smtp.gmail.com:587
|
||||||
|
; QQ: smtp.qq.com:465
|
||||||
|
; Note, if the port ends with "465", SMTPS will be used. Using STARTTLS on port 587 is recommended per RFC 6409. If the server supports STARTTLS it will always be used.
|
||||||
|
HOST =
|
||||||
|
; Disable HELO operation when hostname are different.
|
||||||
|
DISABLE_HELO =
|
||||||
|
; Custom hostname for HELO operation, default is from system.
|
||||||
|
HELO_HOSTNAME =
|
||||||
|
; Do not verify the certificate of the server. Only use this for self-signed certificates
|
||||||
|
SKIP_VERIFY =
|
||||||
|
; Use client certificate
|
||||||
|
USE_CERTIFICATE = false
|
||||||
|
CERT_FILE = custom/mailer/cert.pem
|
||||||
|
KEY_FILE = custom/mailer/key.pem
|
||||||
|
; Mail from address, RFC 5322. This can be just an email address, or the `"Name" <email@example.com>` format
|
||||||
|
FROM =
|
||||||
|
; Mailer user name and password
|
||||||
|
USER =
|
||||||
|
PASSWD =
|
||||||
|
; Send mails as plain text
|
||||||
|
SEND_AS_PLAIN_TEXT = false
|
||||||
|
; Enable sendmail (override SMTP)
|
||||||
|
USE_SENDMAIL = false
|
||||||
|
; 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: not have any config yet
|
||||||
|
; file: session file path, e.g. `data/sessions`
|
||||||
|
; redis: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180
|
||||||
|
; mysql: go-sql-driver/mysql dsn config string, e.g. `root:password@/session_table`
|
||||||
|
PROVIDER_CONFIG = data/sessions
|
||||||
|
; Session cookie name
|
||||||
|
COOKIE_NAME = i_like_gitea
|
||||||
|
; If you use session in https only, default is false
|
||||||
|
COOKIE_SECURE = false
|
||||||
|
; Enable set cookie, default is true
|
||||||
|
ENABLE_SET_COOKIE = true
|
||||||
|
; Session GC time interval 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
|
||||||
|
; Chinese users can choose "duoshuo"
|
||||||
|
; or a custom avatar source, like: http://cn.gravatar.com/avatar/
|
||||||
|
GRAVATAR_SOURCE = gravatar
|
||||||
|
; This value will be forced to be true in offline mode.
|
||||||
|
DISABLE_GRAVATAR = false
|
||||||
|
; Federated avatar lookup uses DNS to discover avatar associated
|
||||||
|
; with emails, see https://www.libravatar.org
|
||||||
|
; This value will be forced to be false in offline mode or Gravatar is disabled.
|
||||||
|
ENABLE_FEDERATED_AVATAR = false
|
||||||
|
|
||||||
|
[attachment]
|
||||||
|
; Whether attachments are enabled. Defaults to `true`
|
||||||
|
ENABLE = true
|
||||||
|
; Path for attachments. Defaults to `data/attachments`
|
||||||
|
PATH = data/attachments
|
||||||
|
; One or more allowed types, e.g. image/jpeg|image/png
|
||||||
|
ALLOWED_TYPES = image/jpeg|image/png|application/zip|application/gzip
|
||||||
|
; Max size of each file. Defaults to 32MB
|
||||||
|
MAX_SIZE = 4
|
||||||
|
; Max number of files per upload. Defaults to 10
|
||||||
|
MAX_FILES = 5
|
||||||
|
|
||||||
|
[time]
|
||||||
|
; Specifies the format for fully 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 channel, keep it as it is if you don't know what it is.
|
||||||
|
BUFFER_LEN = 10000
|
||||||
|
; Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace"
|
||||||
|
LEVEL = Trace
|
||||||
|
|
||||||
|
; For "console" mode only
|
||||||
|
[log.console]
|
||||||
|
LEVEL =
|
||||||
|
|
||||||
|
; For "file" mode only
|
||||||
|
[log.file]
|
||||||
|
LEVEL =
|
||||||
|
; This enables automated log rotate(switch of following options), default is true
|
||||||
|
LOG_ROTATE = true
|
||||||
|
; Max line number of single file, default is 1000000
|
||||||
|
MAX_LINES = 1000000
|
||||||
|
; Max size shift of single file, default is 28 means 1 << 28, 256MB
|
||||||
|
MAX_SIZE_SHIFT = 28
|
||||||
|
; Segment log daily, default is true
|
||||||
|
DAILY_ROTATE = true
|
||||||
|
; Expired days of log file(delete after max days), default is 7
|
||||||
|
MAX_DAYS = 7
|
||||||
|
|
||||||
|
; For "conn" mode only
|
||||||
|
[log.conn]
|
||||||
|
LEVEL =
|
||||||
|
; Reconnect host for every single message, default is false
|
||||||
|
RECONNECT_ON_MSG = false
|
||||||
|
; Try to reconnect when connection is lost, default is false
|
||||||
|
RECONNECT = false
|
||||||
|
; Either "tcp", "unix" or "udp", default is "tcp"
|
||||||
|
PROTOCOL = tcp
|
||||||
|
; Host address
|
||||||
|
ADDR =
|
||||||
|
|
||||||
|
; For "smtp" mode only
|
||||||
|
[log.smtp]
|
||||||
|
LEVEL =
|
||||||
|
; Name displayed in mail title, default is "Diagnostic message from server"
|
||||||
|
SUBJECT = Diagnostic message from server
|
||||||
|
; Mail server
|
||||||
|
HOST =
|
||||||
|
; Mailer user name and password
|
||||||
|
USER =
|
||||||
|
PASSWD =
|
||||||
|
; Receivers, can be one or more, e.g. 1@example.com,2@example.com
|
||||||
|
RECEIVERS =
|
||||||
|
|
||||||
|
; For "database" mode only
|
||||||
|
[log.database]
|
||||||
|
LEVEL =
|
||||||
|
; Either "mysql" or "postgres"
|
||||||
|
DRIVER =
|
||||||
|
; Based on xorm, e.g.: root:root@localhost/gitea?charset=utf8
|
||||||
|
CONN =
|
||||||
|
|
||||||
|
[cron]
|
||||||
|
; Enable running cron tasks periodically.
|
||||||
|
ENABLED = true
|
||||||
|
; Run cron tasks when Gitea starts.
|
||||||
|
RUN_AT_START = false
|
||||||
|
|
||||||
|
; Update mirrors
|
||||||
|
[cron.update_mirrors]
|
||||||
|
SCHEDULE = @every 10m
|
||||||
|
|
||||||
|
; Repository health check
|
||||||
|
[cron.repo_health_check]
|
||||||
|
SCHEDULE = @every 24h
|
||||||
|
TIMEOUT = 60s
|
||||||
|
; Arguments for command 'git fsck', e.g. "--unreachable --tags"
|
||||||
|
; see more on http://git-scm.com/docs/git-fsck/1.7.5
|
||||||
|
ARGS =
|
||||||
|
|
||||||
|
; Check repository statistics
|
||||||
|
[cron.check_repo_stats]
|
||||||
|
RUN_AT_START = true
|
||||||
|
SCHEDULE = @every 24h
|
||||||
|
|
||||||
|
; Clean up old repository archives
|
||||||
|
[cron.archive_cleanup]
|
||||||
|
RUN_AT_START = true
|
||||||
|
SCHEDULE = @every 24h
|
||||||
|
; Archives created more than OLDER_THAN ago are subject to deletion
|
||||||
|
OLDER_THAN = 24h
|
||||||
|
|
||||||
|
; 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 of a single file in diff view
|
||||||
|
MAX_GIT_DIFF_LINES = 1000
|
||||||
|
; Max number of characters of a line allowed in diff view
|
||||||
|
MAX_GIT_DIFF_LINE_CHARACTERS = 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/1.7.5
|
||||||
|
GC_ARGS =
|
||||||
|
|
||||||
|
; Operation timeout in seconds
|
||||||
|
[git.timeout]
|
||||||
|
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]
|
||||||
|
; Max number of items will response in a page
|
||||||
|
MAX_RESPONSE_ITEMS = 50
|
||||||
|
|
||||||
|
[i18n]
|
||||||
|
LANGS = en-US,zh-CN,zh-HK,zh-TW,de-DE,fr-FR,nl-NL,lv-LV,ru-RU,ja-JP,es-ES,pt-BR,pl-PL,bg-BG,it-IT,fi-FI,tr-TR,cs-CZ,sr-SP,sv-SE,ko-KR
|
||||||
|
NAMES = English,简体中文,繁體中文(香港),繁體中文(台灣),Deutsch,Français,Nederlands,Latviešu,Русский,日本語,Español,Português do Brasil,polski,български,Italiano,Suomalainen,Türkçe,čeština,Српски,Svenska,한국어
|
||||||
|
|
||||||
|
; Used for datetimepicker
|
||||||
|
[i18n.datelang]
|
||||||
|
en-US = en
|
||||||
|
zh-CN = zh
|
||||||
|
zh-HK = zh-TW
|
||||||
|
zh-TW = zh-TW
|
||||||
|
de-DE = de
|
||||||
|
fr-FR = fr
|
||||||
|
nl-NL = nl
|
||||||
|
lv-LV = lv
|
||||||
|
ru-RU = ru
|
||||||
|
ja-JP = ja
|
||||||
|
es-ES = es
|
||||||
|
pt-BR = pt-BR
|
||||||
|
pl-PL = pl
|
||||||
|
bg-BG = bg
|
||||||
|
it-IT = it
|
||||||
|
fi-FI = fi
|
||||||
|
tr-TR = tr
|
||||||
|
cs-CZ = cs-CZ
|
||||||
|
sr-SP = sr
|
||||||
|
sv-SE = sv
|
||||||
|
ko-KR = ko
|
||||||
|
|
||||||
|
; Extension mapping to highlight class
|
||||||
|
; e.g. .toml=ini
|
||||||
|
[highlight.mapping]
|
||||||
|
|
||||||
|
[other]
|
||||||
|
SHOW_FOOTER_BRANDING = false
|
||||||
|
; Show version information about Gitea and Go in the footer
|
||||||
|
SHOW_FOOTER_VERSION = true
|
||||||
|
; Show time of template execution in the footer
|
||||||
|
SHOW_FOOTER_TEMPLATE_LOAD_TIME = true
|
||||||
|
|
||||||
|
[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=- -"
|
||||||
|
; Input is not a standard input but a file
|
||||||
|
IS_INPUT_FILE = false
|
|
@ -1,47 +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
|
|
||||||
|
|
||||||
Note, SECTION_NAME in the notation above is case-insensitive.
|
|
||||||
|
|
||||||
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 "." and "_0X2D_" 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 build locally, run:
|
|
||||||
|
|
||||||
go build contrib/environment-to-ini/environment-to-ini.go
|
|
|
@ -1,237 +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"
|
|
||||||
"code.gitea.io/gitea/modules/util"
|
|
||||||
|
|
||||||
"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()
|
|
||||||
isFile, err := util.IsFile(setting.CustomConf)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Unable to check if %s is a file. Error: %v", setting.CustomConf, err)
|
|
||||||
}
|
|
||||||
if isFile {
|
|
||||||
if err := cfg.Append(setting.CustomConf); err != nil {
|
|
||||||
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
|
|
||||||
|
|
||||||
changed := false
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
oldValue := key.Value()
|
|
||||||
if !changed && oldValue != value {
|
|
||||||
changed = true
|
|
||||||
}
|
|
||||||
key.SetValue(value)
|
|
||||||
}
|
|
||||||
destination := c.String("out")
|
|
||||||
if len(destination) == 0 {
|
|
||||||
destination = setting.CustomConf
|
|
||||||
}
|
|
||||||
if destination != setting.CustomConf || changed {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
section = strings.ToLower(section)
|
|
||||||
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")
|
|
||||||
}
|
|
|
@ -19,7 +19,7 @@
|
||||||
"type": "go",
|
"type": "go",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mode": "debug",
|
"mode": "debug",
|
||||||
"buildFlags": "-tags='sqlite sqlite_unlock_notify'",
|
"buildFlags": "-tags=\"sqlite\"",
|
||||||
"port": 2345,
|
"port": 2345,
|
||||||
"host": "127.0.0.1",
|
"host": "127.0.0.1",
|
||||||
"program": "${workspaceRoot}/main.go",
|
"program": "${workspaceRoot}/main.go",
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
"go.buildTags": "'sqlite sqlite_unlock_notify'",
|
|
||||||
"go.testFlags": ["-v"]
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"label": "Build",
|
"taskName": "Build",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "go",
|
"command": "go",
|
||||||
"group": "build",
|
"group": "build",
|
||||||
|
@ -12,19 +12,20 @@
|
||||||
"focus": false,
|
"focus": false,
|
||||||
"panel": "shared"
|
"panel": "shared"
|
||||||
},
|
},
|
||||||
|
"args": ["build"],
|
||||||
"linux": {
|
"linux": {
|
||||||
"args": ["build", "-o", "gitea", "${workspaceRoot}/main.go" ]
|
"args": [ "-o", "gitea", "${workspaceRoot}/main.go" ]
|
||||||
},
|
},
|
||||||
"osx": {
|
"osx": {
|
||||||
"args": ["build", "-o", "gitea", "${workspaceRoot}/main.go" ]
|
"args": [ "-o", "gitea", "${workspaceRoot}/main.go" ]
|
||||||
},
|
},
|
||||||
"windows": {
|
"windows": {
|
||||||
"args": ["build", "-o", "gitea.exe", "\"${workspaceRoot}\\main.go\""]
|
"args": [ "-o", "gitea.exe", "\"${workspaceRoot}\\main.go\""]
|
||||||
},
|
},
|
||||||
"problemMatcher": ["$go"]
|
"problemMatcher": ["$go"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Build (with SQLite3)",
|
"taskName": "Build (with SQLite3)",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "go",
|
"command": "go",
|
||||||
"group": "build",
|
"group": "build",
|
||||||
|
@ -34,14 +35,15 @@
|
||||||
"focus": false,
|
"focus": false,
|
||||||
"panel": "shared"
|
"panel": "shared"
|
||||||
},
|
},
|
||||||
|
"args": ["build", "-tags=\"sqlite\""],
|
||||||
"linux": {
|
"linux": {
|
||||||
"args": ["build", "-tags=\"sqlite sqlite_unlock_notify\"", "-o", "gitea", "${workspaceRoot}/main.go"]
|
"args": ["-o", "gitea", "${workspaceRoot}/main.go"]
|
||||||
},
|
},
|
||||||
"osx": {
|
"osx": {
|
||||||
"args": ["build", "-tags=\"sqlite sqlite_unlock_notify\"", "-o", "gitea", "${workspaceRoot}/main.go"]
|
"args": ["-o", "gitea", "${workspaceRoot}/main.go"]
|
||||||
},
|
},
|
||||||
"windows": {
|
"windows": {
|
||||||
"args": ["build", "-tags=\"sqlite sqlite_unlock_notify\"", "-o", "gitea.exe", "\"${workspaceRoot}\\main.go\""]
|
"args": ["-o", "gitea.exe", "\"${workspaceRoot}\\main.go\""]
|
||||||
},
|
},
|
||||||
"problemMatcher": ["$go"]
|
"problemMatcher": ["$go"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,8 @@
|
||||||
# Default values
|
# Default values
|
||||||
|
|
||||||
NAME=gitea
|
NAME=gitea
|
||||||
GITEA_HOME=/var/lib/${NAME}
|
GITEA_HOME=/home/git/gitea
|
||||||
GITEA_PATH=/usr/local/bin/${NAME}
|
GITEA_PATH=${GITEA_HOME}/$NAME
|
||||||
GITEA_USER=git
|
GITEA_USER=git
|
||||||
SERVICENAME="Gitea - Git with a cup of tea"
|
SERVICENAME="Gitea - Git with a cup of tea"
|
||||||
LOCKFILE=/var/lock/subsys/gitea
|
LOCKFILE=/var/lock/subsys/gitea
|
||||||
|
@ -49,11 +49,11 @@ DAEMON_OPTS="--check $NAME"
|
||||||
start() {
|
start() {
|
||||||
cd ${GITEA_HOME}
|
cd ${GITEA_HOME}
|
||||||
echo -n "Starting ${SERVICENAME}: "
|
echo -n "Starting ${SERVICENAME}: "
|
||||||
daemon $DAEMON_OPTS "${GITEA_PATH} web -c /etc/${NAME}/app.ini > ${LOGFILE} 2>&1 &"
|
daemon $DAEMON_OPTS "${GITEA_PATH} web > ${LOGFILE} 2>&1 &"
|
||||||
RETVAL=$?
|
RETVAL=$?
|
||||||
echo
|
echo
|
||||||
[ $RETVAL = 0 ] && touch ${LOCKFILE}
|
[ $RETVAL = 0 ] && touch ${LOCKFILE}
|
||||||
|
|
||||||
return $RETVAL
|
return $RETVAL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ stop() {
|
||||||
killproc ${NAME}
|
killproc ${NAME}
|
||||||
RETVAL=$?
|
RETVAL=$?
|
||||||
echo
|
echo
|
||||||
[ $RETVAL = 0 ] && rm -f ${LOCKFILE}
|
[ $RETVAL = 0 ] && rm -f ${LOCKFILE}
|
||||||
}
|
}
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
|
|
|
@ -14,20 +14,17 @@
|
||||||
# Do NOT "set -e"
|
# Do NOT "set -e"
|
||||||
|
|
||||||
# PATH should only include /usr/* if it runs after the mountnfs.sh script
|
# PATH should only include /usr/* if it runs after the mountnfs.sh script
|
||||||
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
|
PATH=/sbin:/usr/sbin:/bin:/usr/bin
|
||||||
DESC="Gitea - Git with a cup of tea"
|
DESC="Git with a cup of tea"
|
||||||
NAME=gitea
|
NAME=gitea
|
||||||
SERVICEVERBOSE=yes
|
SERVICEVERBOSE=yes
|
||||||
PIDFILE=/run/$NAME.pid
|
PIDFILE=/var/run/$NAME.pid
|
||||||
SCRIPTNAME=/etc/init.d/$NAME
|
SCRIPTNAME=/etc/init.d/$NAME
|
||||||
WORKINGDIR=/var/lib/$NAME
|
WORKINGDIR=/home/git/gitea
|
||||||
DAEMON=/usr/local/bin/$NAME
|
DAEMON=$WORKINGDIR/$NAME
|
||||||
DAEMON_ARGS="web -c /etc/$NAME/app.ini"
|
DAEMON_ARGS="web"
|
||||||
USER=git
|
USER=git
|
||||||
USERBIND=""
|
USERBIND="setcap cap_net_bind_service=+ep"
|
||||||
# If you want to bind Gitea to a port below 1024 uncomment
|
|
||||||
# the line below
|
|
||||||
#USERBIND="setcap cap_net_bind_service=+ep"
|
|
||||||
STOP_SCHEDULE="${STOP_SCHEDULE:-QUIT/5/TERM/1/KILL/5}"
|
STOP_SCHEDULE="${STOP_SCHEDULE:-QUIT/5/TERM/1/KILL/5}"
|
||||||
|
|
||||||
# Read configuration variable file if it is present
|
# Read configuration variable file if it is present
|
||||||
|
@ -39,7 +36,7 @@ STOP_SCHEDULE="${STOP_SCHEDULE:-QUIT/5/TERM/1/KILL/5}"
|
||||||
do_start()
|
do_start()
|
||||||
{
|
{
|
||||||
$USERBIND $DAEMON
|
$USERBIND $DAEMON
|
||||||
sh -c "USER=$USER HOME=/home/$USER GITEA_WORK_DIR=$WORKINGDIR start-stop-daemon --start --quiet --pidfile $PIDFILE --make-pidfile \\
|
sh -c "USER=$USER start-stop-daemon --start --quiet --pidfile $PIDFILE --make-pidfile \\
|
||||||
--background --chdir $WORKINGDIR --chuid $USER \\
|
--background --chdir $WORKINGDIR --chuid $USER \\
|
||||||
--exec $DAEMON -- $DAEMON_ARGS"
|
--exec $DAEMON -- $DAEMON_ARGS"
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,9 @@ load_rc_config $name
|
||||||
|
|
||||||
: ${gitea_user:="git"}
|
: ${gitea_user:="git"}
|
||||||
: ${gitea_enable:="NO"}
|
: ${gitea_enable:="NO"}
|
||||||
: ${gitea_directory:="/var/lib/gitea"}
|
: ${gitea_directory:="/home/git"}
|
||||||
|
|
||||||
command="/usr/local/bin/gitea web -c /etc/gitea/app.ini"
|
command="${gitea_directory}/gitea web"
|
||||||
procname="$(echo $command |cut -d' ' -f1)"
|
procname="$(echo $command |cut -d' ' -f1)"
|
||||||
|
|
||||||
pidfile="${gitea_directory}/${name}.pid"
|
pidfile="${gitea_directory}/${name}.pid"
|
||||||
|
@ -33,7 +33,6 @@ gitea_start() {
|
||||||
cd ${gitea_directory}
|
cd ${gitea_directory}
|
||||||
export USER=${gitea_user}
|
export USER=${gitea_user}
|
||||||
export HOME=/usr/home/${gitea_user}
|
export HOME=/usr/home/${gitea_user}
|
||||||
export GITEA_WORK_DIR=${gitea_directory}
|
|
||||||
/usr/sbin/daemon -f -u ${gitea_user} -p ${pidfile} $command
|
/usr/sbin/daemon -f -u ${gitea_user} -p ${pidfile} $command
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
#!/sbin/openrc-run
|
#!/sbin/openrc-run
|
||||||
|
|
||||||
DIR=/var/lib/gitea
|
DIR=/home/git/gitea
|
||||||
USER=git
|
USER=git
|
||||||
|
|
||||||
start_stop_daemon_args="--user ${USER} --chdir ${DIR}"
|
start_stop_daemon_args="--user ${USER} --chdir ${DIR}"
|
||||||
command="/usr/local/bin/gitea"
|
command="${DIR}/gitea"
|
||||||
command_args="web -c /etc/gitea/app.ini"
|
command_args="web"
|
||||||
command_background=yes
|
command_background=yes
|
||||||
pidfile=/run/gitea.pid
|
pidfile=/var/run/gitea.pid
|
||||||
|
|
||||||
depend()
|
depend()
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
#
|
#
|
||||||
# $OpenBSD$
|
# $OpenBSD$
|
||||||
|
|
||||||
daemon="/usr/local/bin/gitea"
|
daemon="/home/git/gitea/gitea"
|
||||||
daemon_user="git"
|
daemon_user="git"
|
||||||
daemon_flags="web -c /etc/gitea/app.ini"
|
daemon_flags="web"
|
||||||
|
|
||||||
gitea_directory="/var/lib/gitea"
|
gitea_directory="/home/git/gitea"
|
||||||
|
|
||||||
rc_bg=YES
|
rc_bg=YES
|
||||||
|
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
|
|
||||||
<service_bundle type="manifest" name="export">
|
|
||||||
<service name="gitea" type="service" version="1">
|
|
||||||
<create_default_instance enabled="false"/>
|
|
||||||
|
|
||||||
<dependency name="network" grouping="require_all" restart_on="refresh" type="service">
|
|
||||||
<service_fmri value="svc:/milestone/network:default"/>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency name="filesystem" grouping="require_all" restart_on="refresh" type="service">
|
|
||||||
<service_fmri value="svc:/system/filesystem/local"/>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<exec_method
|
|
||||||
type="method"
|
|
||||||
name="start"
|
|
||||||
exec="/opt/local/bin/gitea web"
|
|
||||||
timeout_seconds="60">
|
|
||||||
<method_context>
|
|
||||||
<method_credential user="git" group="git" />
|
|
||||||
<method_environment>
|
|
||||||
<envvar name='GITEA_WORK_DIR' value='/opt/local/share/gitea'/>
|
|
||||||
<envvar name='GITEA_CUSTOM' value='/opt/local/etc/gitea'/>
|
|
||||||
<envvar name='HOME' value='/var/db/gitea'/>
|
|
||||||
<envvar name='PATH' value='/opt/local/bin:${PATH}'/>
|
|
||||||
<envvar name='USER' value='git'/>
|
|
||||||
</method_environment>
|
|
||||||
</method_context>
|
|
||||||
</exec_method>
|
|
||||||
<exec_method type="method" name="stop" exec=":kill" timeout_seconds="60"/>
|
|
||||||
|
|
||||||
<property_group name="application" type="application"></property_group>
|
|
||||||
<property_group name="startd" type="framework">
|
|
||||||
<propval name="duration" type="astring" value="child"/>
|
|
||||||
<propval name="ignore_error" type="astring" value="core,signal"/>
|
|
||||||
</property_group>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<common_name>
|
|
||||||
<loctext xml:lang="C">A painless, self-hosted Git service</loctext>
|
|
||||||
</common_name>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
</service>
|
|
||||||
</service_bundle>
|
|
|
@ -18,10 +18,10 @@
|
||||||
# Default values
|
# Default values
|
||||||
|
|
||||||
NAME=gitea
|
NAME=gitea
|
||||||
GITEA_HOME=/var/lib/$NAME
|
GITEA_HOME=/home/git/gitea
|
||||||
GITEA_PATH=/usr/local/bin/$NAME
|
GITEA_PATH=${GITEA_HOME}/$NAME
|
||||||
GITEA_USER=git
|
GITEA_USER=git
|
||||||
SERVICENAME="Gitea - Git with a cup of tea"
|
SERVICENAME="Git - with a cup of tea"
|
||||||
LOCKFILE=/var/lock/subsys/gitea
|
LOCKFILE=/var/lock/subsys/gitea
|
||||||
LOGPATH=${GITEA_HOME}/log
|
LOGPATH=${GITEA_HOME}/log
|
||||||
LOGFILE=${LOGPATH}/error.log
|
LOGFILE=${LOGPATH}/error.log
|
||||||
|
@ -58,7 +58,7 @@ case "$1" in
|
||||||
# return skipped as service is already running
|
# return skipped as service is already running
|
||||||
(exit 5)
|
(exit 5)
|
||||||
else
|
else
|
||||||
su - ${GITEA_USER} -c "USER=${GITEA_USER} GITEA_WORK_DIR=${GITEA_HOME} ${GITEA_PATH} web -c /etc/${NAME}/app.ini 2>&1 >>${LOGFILE} &"
|
su - ${GITEA_USER} -c "USER=${GITEA_USER} ${GITEA_PATH} web 2>&1 >>${LOGFILE} &"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Remember status and be verbose
|
# Remember status and be verbose
|
||||||
|
@ -93,7 +93,7 @@ case "$1" in
|
||||||
|
|
||||||
# Return value is slightly different for the status command:
|
# Return value is slightly different for the status command:
|
||||||
# 0 - service up and running
|
# 0 - service up and running
|
||||||
# 1 - service dead, but /run/ pid file exists
|
# 1 - service dead, but /var/run/ pid file exists
|
||||||
# 2 - service dead, but /var/lock/ lock file exists
|
# 2 - service dead, but /var/lock/ lock file exists
|
||||||
# 3 - service not running (unused)
|
# 3 - service not running (unused)
|
||||||
# 4 - service status unknown :-(
|
# 4 - service status unknown :-(
|
||||||
|
|
|
@ -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>
|
|
|
@ -0,0 +1,206 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
gitea_version=1.0.1
|
||||||
|
tested_gogs_version="0.9.114.1227"
|
||||||
|
gogs_binary=gogs
|
||||||
|
gitea_binary=gitea
|
||||||
|
download_gitea=true
|
||||||
|
gitea_path=
|
||||||
|
|
||||||
|
function usage() {
|
||||||
|
echo "Optional parameters: [-b Gitea binary] [-i Gitea install dir] [-o gogs binary] [-h help]";
|
||||||
|
exit 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while getopts ":b::i:o:h:" opt; do
|
||||||
|
case $opt in
|
||||||
|
b)
|
||||||
|
gitea_binary=${OPTARG}
|
||||||
|
download_gitea=false
|
||||||
|
;;
|
||||||
|
i)
|
||||||
|
gitea_path=${OPTARG}
|
||||||
|
;;
|
||||||
|
o)
|
||||||
|
gogs_binary=${OPTARG}
|
||||||
|
;;
|
||||||
|
h)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
\?)
|
||||||
|
echo -e "Invalid option: -$OPTARG"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
:)
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
function exitOnError() {
|
||||||
|
if [ "$?" != "0" ]; then
|
||||||
|
echo -e $1
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkBinary() {
|
||||||
|
if [ ! -f $1 ]; then
|
||||||
|
echo "Unable to find $1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function continueYN(){
|
||||||
|
while true; do
|
||||||
|
echo -e "$1 Yes or No"
|
||||||
|
read yn
|
||||||
|
case $yn in
|
||||||
|
[Yy]* ) break;;
|
||||||
|
[Nn]* ) exit 1;;
|
||||||
|
* ) echo "Please answer yes or no.";;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
########## Binary checks
|
||||||
|
if pidof "$gogs_binary" >/dev/null; then
|
||||||
|
echo "Please stop gogs before migrating to Gitea"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
checkBinary "$gogs_binary"
|
||||||
|
|
||||||
|
if [ ! -x "$gogs_binary" ]; then
|
||||||
|
echo "Please make sure that you are running this script as the gogs user"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
########## Version check
|
||||||
|
gogs_version=$(./$gogs_binary --version)
|
||||||
|
original_IFS=$IFS
|
||||||
|
IFS="." && current_version=(${gogs_version#"Gogs version "}) && minimal_version=($tested_gogs_version)
|
||||||
|
IFS=$original_IFS
|
||||||
|
|
||||||
|
count=0
|
||||||
|
for i in "${current_version[@]}"
|
||||||
|
do
|
||||||
|
if [ $i -gt ${minimal_version[$count]} ]; then
|
||||||
|
echo -e "!!!--WARNING--!!!\nYour $gogs_version is newer than the tested Gogs version $tested_gogs_version\nUse this script on your own risk\n!!!--WARNING--!!!"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
let count+=1
|
||||||
|
done
|
||||||
|
|
||||||
|
########## Disclaimer
|
||||||
|
continueYN "This migration script creates a backup before it starts with the actual migration
|
||||||
|
If something goes wrong you could always resotre this backup.
|
||||||
|
The backups are stored into your gogs folder in gogs-dump-[timestamp].zip file
|
||||||
|
|
||||||
|
Migrating from gogs to gitea, are you sure?"
|
||||||
|
|
||||||
|
########## gogs dump
|
||||||
|
echo "Creating a backup of gogs, this could take a while..."
|
||||||
|
./"$gogs_binary" dump
|
||||||
|
exitOnError "Failed to create a gogs dump"
|
||||||
|
|
||||||
|
########## Create Gitea folder
|
||||||
|
if [ -z "$gitea_path" ]; then
|
||||||
|
echo "Where do you want to install Gitea?"
|
||||||
|
read gitea_path
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$gitea_path" ]; then
|
||||||
|
mkdir -p "$gitea_path"
|
||||||
|
exitOnError
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(ls -A $gitea_path)" ]; then
|
||||||
|
continueYN "!!!--WARNING--!!!\nDirectory $gitea_path is not empty, do you want to continue?"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
########## Download Gitea
|
||||||
|
if [ $download_gitea == true ]; then
|
||||||
|
|
||||||
|
########## Detect os
|
||||||
|
case "$OSTYPE" in
|
||||||
|
darwin*) platform="darwin-10.6";;
|
||||||
|
linux*) platform="linux" ;;
|
||||||
|
freebsd*) platform="bsd" ;;
|
||||||
|
netbsd*) platform="bsd" ;;
|
||||||
|
openbsd*) platform="bsd" ;;
|
||||||
|
*) echo "Unsupported os: $OSTYPE\n Please download/compile your own binary and run this script with the -b option" exit 1;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
arch=""
|
||||||
|
bits=""
|
||||||
|
if [[ "$platform" == "linux" ]] || [[ "$platform" == "bsd" ]]; then
|
||||||
|
arch="$(uname -m | sed -e 's/arm\(.*\)/arm-\1/' -e s/aarch64.*/arm64/)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$platform" == "bsd" ]] && [[ "$arch" != "arm"* ]]; then
|
||||||
|
echo "Currently Gitea only supports arm prebuilt binarys on bsd"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$arch" != "arm"* ]] && [[ "$arch" != "mips"* ]]; then
|
||||||
|
arch=""
|
||||||
|
case "$(getconf LONG_BIT)" in
|
||||||
|
64*) bits="amd64";;
|
||||||
|
32*) bits="386" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
########## Wget Gitea
|
||||||
|
echo "Downloading Gitea"
|
||||||
|
file="gitea-$gitea_version-$platform-$arch$bits"
|
||||||
|
url="https://dl.gitea.io/gitea/$gitea_version/$file"
|
||||||
|
wget "$url" -P "$gitea_path"
|
||||||
|
exitOnError "Failed to download $url"
|
||||||
|
|
||||||
|
wget "$url.sha256" -P "$gitea_path"
|
||||||
|
exitOnError "Failed to Gitea checksum $url.sha256"
|
||||||
|
|
||||||
|
echo "Comparing checksums"
|
||||||
|
gogs_dir=$(pwd)
|
||||||
|
cd "$gitea_path"
|
||||||
|
|
||||||
|
sha256sum -c "$file.sha256"
|
||||||
|
exitOnError "Downloaded Gitea checksums do not match"
|
||||||
|
|
||||||
|
rm "$file.sha256"
|
||||||
|
mv "$file" gitea
|
||||||
|
cd "$gogs_dir"
|
||||||
|
|
||||||
|
else
|
||||||
|
checkBinary "$gitea_binary"
|
||||||
|
if [ "$gitea_binary" != "$gitea_path/gitea" ];then
|
||||||
|
cp "$gitea_binary" "$gitea_path/gitea"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
########## Copy gogs data to Gitea folder
|
||||||
|
echo "Copying gogs data to Gitea, this could take a while..."
|
||||||
|
cp -R custom "$gitea_path"
|
||||||
|
cp -R data "$gitea_path"
|
||||||
|
#cp -R conf "$gitea_path"
|
||||||
|
|
||||||
|
########## Moving & deleting old files
|
||||||
|
#mv $gitea_path/conf $gitea_path/options
|
||||||
|
cd "$gitea_path"
|
||||||
|
mv "custom/conf/app.ini" "custom/conf/gogs_app.ini"
|
||||||
|
url="https://raw.githubusercontent.com/go-gitea/gitea/v$gitea_version/conf/app.ini"
|
||||||
|
wget "$url" -P "custom/conf/"
|
||||||
|
exitOnError "Unable to download Gitea app.ini"
|
||||||
|
rm -f conf/README.md
|
||||||
|
|
||||||
|
echo -e "Migration is almost complete, you only need to merge custom/conf/gogs_app.ini into custom/conf/app.ini"
|
||||||
|
continueYN "Do you want to start Gitea?"
|
||||||
|
|
||||||
|
########## Starting Gitea
|
||||||
|
echo "Starting Gitea"
|
||||||
|
chmod +x gitea
|
||||||
|
./gitea web
|
||||||
|
exitOnError "Failed to start Gitea"
|
|
@ -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,268 +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
|
|
||||||
|
|
||||||
/*
|
|
||||||
Checkout a PR and load the tests data into sqlite database
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"os/user"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
gitea_git "code.gitea.io/gitea/modules/git"
|
|
||||||
"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"
|
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5"
|
|
||||||
"github.com/go-git/go-git/v5/config"
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
|
||||||
context2 "github.com/gorilla/context"
|
|
||||||
"xorm.io/xorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
var codeFilePath = "contrib/pr/checkout.go"
|
|
||||||
|
|
||||||
func runPR() {
|
|
||||||
log.Printf("[PR] Starting gitea ...\n")
|
|
||||||
curDir, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
setting.SetCustomPathAndConf("", "", "")
|
|
||||||
setting.NewContext()
|
|
||||||
|
|
||||||
setting.RepoRootPath, err = ioutil.TempDir(os.TempDir(), "repos")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("TempDir: %v\n", err)
|
|
||||||
}
|
|
||||||
setting.AppDataPath, err = ioutil.TempDir(os.TempDir(), "appdata")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("TempDir: %v\n", err)
|
|
||||||
}
|
|
||||||
setting.AppWorkPath = curDir
|
|
||||||
setting.StaticRootPath = curDir
|
|
||||||
setting.GravatarSourceURL, err = url.Parse("https://secure.gravatar.com/avatar/")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("url.Parse: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
setting.AppURL = "http://localhost:8080/"
|
|
||||||
setting.HTTPPort = "8080"
|
|
||||||
setting.SSH.Domain = "localhost"
|
|
||||||
setting.SSH.Port = 3000
|
|
||||||
setting.InstallLock = true
|
|
||||||
setting.SecretKey = "9pCviYTWSb"
|
|
||||||
setting.InternalToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTI3OTU5ODN9.OQkH5UmzID2XBdwQ9TAI6Jj2t1X-wElVTjbE7aoN4I8"
|
|
||||||
curUser, err := user.Current()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
setting.RunUser = curUser.Username
|
|
||||||
|
|
||||||
log.Printf("[PR] Loading fixtures data ...\n")
|
|
||||||
gitea_git.CheckLFSVersion()
|
|
||||||
//models.LoadConfigs()
|
|
||||||
/*
|
|
||||||
setting.Database.Type = "sqlite3"
|
|
||||||
setting.Database.Path = ":memory:"
|
|
||||||
setting.Database.Timeout = 500
|
|
||||||
*/
|
|
||||||
db := setting.Cfg.Section("database")
|
|
||||||
db.NewKey("DB_TYPE", "sqlite3")
|
|
||||||
db.NewKey("PATH", ":memory:")
|
|
||||||
|
|
||||||
routers.NewServices()
|
|
||||||
setting.Database.LogSQL = true
|
|
||||||
//x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
|
|
||||||
|
|
||||||
models.NewEngine(context.Background(), func(_ *xorm.Engine) error {
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
models.HasEngine = true
|
|
||||||
//x.ShowSQL(true)
|
|
||||||
err = models.InitFixtures(
|
|
||||||
path.Join(curDir, "models/fixtures/"),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Error initializing test database: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
models.LoadFixtures()
|
|
||||||
util.RemoveAll(setting.RepoRootPath)
|
|
||||||
util.RemoveAll(models.LocalCopyPath())
|
|
||||||
util.CopyDir(path.Join(curDir, "integrations/gitea-repositories-meta"), setting.RepoRootPath)
|
|
||||||
|
|
||||||
log.Printf("[PR] Setting up router\n")
|
|
||||||
//routers.GlobalInit()
|
|
||||||
external.RegisterRenderers()
|
|
||||||
markup.Init()
|
|
||||||
c := routers.NormalRoutes()
|
|
||||||
|
|
||||||
log.Printf("[PR] Ready for testing !\n")
|
|
||||||
log.Printf("[PR] Login with user1, user2, user3, ... with pass: password\n")
|
|
||||||
/*
|
|
||||||
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
|
|
||||||
|
|
||||||
if setting.LFS.StartServer {
|
|
||||||
log.Info("LFS server enabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
if setting.EnablePprof {
|
|
||||||
go func() {
|
|
||||||
log.Info("Starting pprof server on localhost:6060")
|
|
||||||
log.Info("%v", http.ListenAndServe("localhost:6060", nil))
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
//Start the server
|
|
||||||
http.ListenAndServe(":8080", context2.ClearHandler(c))
|
|
||||||
|
|
||||||
log.Printf("[PR] Cleaning up ...\n")
|
|
||||||
/*
|
|
||||||
if err = util.RemoveAll(setting.Indexer.IssuePath); err != nil {
|
|
||||||
fmt.Printf("util.RemoveAll: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
if err = util.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 = util.RemoveAll(setting.AppDataPath); err != nil {
|
|
||||||
log.Fatalf("util.RemoveAll: %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var runPRFlag = flag.Bool("run", false, "Run the PR code")
|
|
||||||
flag.Parse()
|
|
||||||
if *runPRFlag {
|
|
||||||
runPR()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// To force checkout (e.g. Windows complains about unclean work tree) set env variable FORCE=true
|
|
||||||
force, err := strconv.ParseBool(os.Getenv("FORCE"))
|
|
||||||
if err != nil {
|
|
||||||
force = false
|
|
||||||
}
|
|
||||||
|
|
||||||
//Otherwise checkout PR
|
|
||||||
if len(os.Args) != 2 {
|
|
||||||
log.Fatal("Need only one arg: the PR number")
|
|
||||||
}
|
|
||||||
pr := os.Args[1]
|
|
||||||
|
|
||||||
codeFilePath = filepath.FromSlash(codeFilePath) //Convert to running OS
|
|
||||||
|
|
||||||
//Copy this file if it will not exist in the PR branch
|
|
||||||
dat, err := ioutil.ReadFile(codeFilePath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to cache this code file : %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
repo, err := git.PlainOpen(".")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to open the repo : %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Find remote upstream
|
|
||||||
remotes, err := repo.Remotes()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to list remotes of repo : %v", err)
|
|
||||||
}
|
|
||||||
remoteUpstream := "origin" //Default
|
|
||||||
for _, r := range remotes {
|
|
||||||
if r.Config().URLs[0] == "https://github.com/go-gitea/gitea.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
|
|
||||||
remoteUpstream = r.Config().Name
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
branch := fmt.Sprintf("pr-%s-%d", pr, time.Now().Unix())
|
|
||||||
branchRef := plumbing.NewBranchReferenceName(branch)
|
|
||||||
|
|
||||||
log.Printf("Fetching PR #%s in %s\n", pr, branch)
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
//Use git cli command for windows
|
|
||||||
runCmd("git", "fetch", remoteUpstream, fmt.Sprintf("pull/%s/head:%s", pr, branch))
|
|
||||||
} else {
|
|
||||||
ref := fmt.Sprintf("refs/pull/%s/head:%s", pr, branchRef)
|
|
||||||
err = repo.Fetch(&git.FetchOptions{
|
|
||||||
RemoteName: remoteUpstream,
|
|
||||||
RefSpecs: []config.RefSpec{
|
|
||||||
config.RefSpec(ref),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to fetch %s from %s : %v", ref, remoteUpstream, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tree, err := repo.Worktree()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to parse git tree : %v", err)
|
|
||||||
}
|
|
||||||
log.Printf("Checkout PR #%s in %s\n", pr, branch)
|
|
||||||
err = tree.Checkout(&git.CheckoutOptions{
|
|
||||||
Branch: branchRef,
|
|
||||||
Force: force,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to checkout %s : %v", branch, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Copy this file if not exist
|
|
||||||
if _, err := os.Stat(codeFilePath); os.IsNotExist(err) {
|
|
||||||
err = os.MkdirAll(filepath.Dir(codeFilePath), 0755)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to duplicate this code file in PR : %v", err)
|
|
||||||
}
|
|
||||||
err = ioutil.WriteFile(codeFilePath, dat, 0644)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to duplicate this code file in PR : %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Force build of js, css, bin, ...
|
|
||||||
runCmd("make", "build")
|
|
||||||
//Start with integration test
|
|
||||||
runCmd("go", "run", "-mod", "vendor", "-tags", "sqlite sqlite_unlock_notify", codeFilePath, "-run")
|
|
||||||
}
|
|
||||||
func runCmd(cmd ...string) {
|
|
||||||
log.Printf("Executing : %s ...\n", cmd)
|
|
||||||
c := exec.Command(cmd[0], cmd[1:]...)
|
|
||||||
c.Stdout = os.Stdout
|
|
||||||
c.Stderr = os.Stderr
|
|
||||||
if err := c.Start(); err != nil {
|
|
||||||
log.Panicln(err)
|
|
||||||
}
|
|
||||||
if err := c.Wait(); err != nil {
|
|
||||||
log.Panicln(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,51 +2,10 @@
|
||||||
Description=Gitea (Git with a cup of tea)
|
Description=Gitea (Git with a cup of tea)
|
||||||
After=syslog.target
|
After=syslog.target
|
||||||
After=network.target
|
After=network.target
|
||||||
###
|
#After=mysqld.service
|
||||||
# Don't forget to add the database service dependencies
|
|
||||||
###
|
|
||||||
#
|
|
||||||
#Wants=mysql.service
|
|
||||||
#After=mysql.service
|
|
||||||
#
|
|
||||||
#Wants=mariadb.service
|
|
||||||
#After=mariadb.service
|
|
||||||
#
|
|
||||||
#Wants=postgresql.service
|
|
||||||
#After=postgresql.service
|
#After=postgresql.service
|
||||||
#
|
|
||||||
#Wants=memcached.service
|
|
||||||
#After=memcached.service
|
#After=memcached.service
|
||||||
#
|
|
||||||
#Wants=redis.service
|
|
||||||
#After=redis.service
|
#After=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]
|
[Service]
|
||||||
# Modify these two values and uncomment them if you have
|
# Modify these two values and uncomment them if you have
|
||||||
|
@ -55,29 +14,13 @@ After=network.target
|
||||||
###
|
###
|
||||||
#LimitMEMLOCK=infinity
|
#LimitMEMLOCK=infinity
|
||||||
#LimitNOFILE=65535
|
#LimitNOFILE=65535
|
||||||
RestartSec=2s
|
|
||||||
Type=simple
|
Type=simple
|
||||||
User=git
|
User=git
|
||||||
Group=git
|
Group=git
|
||||||
WorkingDirectory=/var/lib/gitea/
|
WorkingDirectory=/home/git/gitea
|
||||||
# If using Unix socket: tells systemd to create the /run/gitea folder, which will contain the gitea.sock file
|
ExecStart=/home/git/gitea/gitea web
|
||||||
# (manually creating /run/gitea doesn't work, because it would not persist across reboots)
|
|
||||||
#RuntimeDirectory=gitea
|
|
||||||
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
|
|
||||||
Restart=always
|
Restart=always
|
||||||
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
|
Environment=USER=git HOME=/home/git
|
||||||
# If you install Git to directory prefix other than default PATH (which happens
|
|
||||||
# for example if you install other versions of Git side-to-side with
|
|
||||||
# distribution version), uncomment below line and add that prefix to PATH
|
|
||||||
# Don't forget to place git-lfs binary on the PATH below if you want to enable
|
|
||||||
# Git LFS support
|
|
||||||
#Environment=PATH=/path/to/git/bin:/bin:/sbin:/usr/bin:/usr/sbin
|
|
||||||
# If you want to bind Gitea to a port below 1024, uncomment
|
|
||||||
# the two values below, or use socket activation to pass Gitea its ports as above
|
|
||||||
###
|
|
||||||
#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
|
||||||
#AmbientCapabilities=CAP_NET_BIND_SERVICE
|
|
||||||
###
|
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
grep 'git' go.mod | grep '\.com' | grep -v indirect | grep -v replace | cut -f 2 | cut -d ' ' -f 1 | while read line; do
|
|
||||||
go get -u "$line"
|
|
||||||
make vendor
|
|
||||||
git add .
|
|
||||||
git commit -S -m "update $line"
|
|
||||||
done
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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/)
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/bash
|
||||||
|
export GITEA_CUSTOM=/data/gitea
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
[[ -f ./setup ]] && source ./setup
|
||||||
|
|
||||||
|
pushd /app/gitea > /dev/null
|
||||||
|
exec su-exec git /app/gitea/gitea web
|
||||||
|
popd
|
|
@ -0,0 +1,41 @@
|
||||||
|
#!/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"} \
|
||||||
|
SECRET_KEY=${SECRET_KEY:-""} \
|
||||||
|
envsubst < /etc/templates/app.ini > /data/gitea/conf/app.ini
|
||||||
|
fi
|
||||||
|
|
||||||
|
chown -R git:git /data/gitea /app/gitea /data/git
|
||||||
|
chmod 0755 /data/gitea /app/gitea /data/git
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/bash
|
||||||
|
exit 0
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
[[ -f ./setup ]] && source ./setup
|
||||||
|
|
||||||
|
pushd /root > /dev/null
|
||||||
|
exec su-exec root /usr/sbin/sshd -D
|
||||||
|
popd
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue