mirror of
https://github.com/go-gitea/gitea.git
synced 2025-08-09 20:11:44 +02:00
Compare commits
166 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
75496b9ff5 | ||
|
8dad47a94a | ||
|
8e792986bb | ||
|
da80e90ac8 | ||
|
74dc22358b | ||
|
7d3e174906 | ||
|
8456700411 | ||
|
8a6acbbc12 | ||
|
98b3d8d5e1 | ||
|
e663f7459a | ||
|
7e85cba3e5 | ||
|
26628aa1d1 | ||
|
d9d2e8f1e8 | ||
|
4558eeb21a | ||
|
be25afc6de | ||
|
90bf1e7961 | ||
|
77ce08976d | ||
|
8f389c5dfa | ||
|
edef62e69e | ||
|
cdff144f76 | ||
|
ad6084a222 | ||
|
d3200db041 | ||
|
f305cffcaf | ||
|
c0320065b6 | ||
|
a1b74c5509 | ||
|
101fb0d7e2 | ||
|
82637c240a | ||
|
d0174d45ed | ||
|
da7a525c5c | ||
|
014313134f | ||
|
7dddf2186b | ||
|
446c06b817 | ||
|
9569607abb | ||
|
8ff4f82e05 | ||
|
2595c70868 | ||
|
00dc35e2de | ||
|
841efac895 | ||
|
dd827d6f2f | ||
|
4d2a6c40f8 | ||
|
fb274ec54b | ||
|
0c3f95034a | ||
|
4583caa077 | ||
|
cf20ebc8ba | ||
|
5ee09d3c81 | ||
|
e846b712fc | ||
|
49d113945f | ||
|
096aa18249 | ||
|
bf853db450 | ||
|
fb656b5124 | ||
|
4be59eb5d9 | ||
|
450b32c1a1 | ||
|
06673cbccb | ||
|
2fd708a397 | ||
|
7a0a133d7c | ||
|
17022f8b62 | ||
|
5568dd6475 | ||
|
58c105d4bf | ||
|
afa7f22dd8 | ||
|
182be90655 | ||
|
4a738a8f16 | ||
|
206b66a184 | ||
|
205be63bc1 | ||
|
bf1441b1e1 | ||
|
fae18bdac0 | ||
|
661e3e2bdc | ||
|
70038719bf | ||
|
55d7e53d99 | ||
|
96d41287e5 | ||
|
df11075389 | ||
|
b8a2cd9f40 | ||
|
4f296f7436 | ||
|
78b9ef3586 | ||
|
90dfe445c2 | ||
|
a728d1e046 | ||
|
7f85728cf9 | ||
|
d2b308ae35 | ||
|
8e8e8ee150 | ||
|
05ee88e576 | ||
|
0d7cb2323f | ||
|
5cdffc2b0c | ||
|
a0101c61a4 | ||
|
c0b1197a64 | ||
|
e39ed0b1d9 | ||
|
cb24cbc1fc | ||
|
584d01cf2c | ||
|
798fdeae45 | ||
|
87997cccbb | ||
|
0d5111c5c3 | ||
|
10fff12da4 | ||
|
0d43a2a069 | ||
|
8396b792f8 | ||
|
d551152582 | ||
|
f677ed628b | ||
|
07629bd55c | ||
|
d475b656b1 | ||
|
6e14773c44 | ||
|
25421f08c0 | ||
|
bdb491e764 | ||
|
a82c7d4323 | ||
|
7ec1c13f53 | ||
|
4c9d00cf78 | ||
|
33431fcbd3 | ||
|
f2a3a9117e | ||
|
ef7a52826d | ||
|
e0d28e2026 | ||
|
2f6dad2e34 | ||
|
bcde51f4c2 | ||
|
ed3a4cd103 | ||
|
c6ab79ee3c | ||
|
48fca01b0d | ||
|
9a8e02ce30 | ||
|
159a4db30a | ||
|
b4d18dae19 | ||
|
ee0097f97d | ||
|
122f8f86d5 | ||
|
1f72656892 | ||
|
5a32224a2c | ||
|
8049de82f9 | ||
|
797cb38a4a | ||
|
ae4955999e | ||
|
1e446bb176 | ||
|
9aa580ce0e | ||
|
3421e4b756 | ||
|
6086a9061b | ||
|
4ad10ac015 | ||
|
cbdbae2925 | ||
|
350c10fe5b | ||
|
02259a0f3a | ||
|
c3e752ae29 | ||
|
3f94dffca1 | ||
|
52b4b984a5 | ||
|
77a2d75639 | ||
|
79d9cda993 | ||
|
02edb9df52 | ||
|
f825e2a568 | ||
|
8e38bd154f | ||
|
0b0456310f | ||
|
639c737648 | ||
|
adfe13f1a2 | ||
|
47cb9b3de2 | ||
|
28133a801a | ||
|
3d272b899d | ||
|
5178aa2130 | ||
|
5da8a84328 | ||
|
d795bfc964 | ||
|
151daf73a6 | ||
|
e177728a82 | ||
|
074f7abd95 | ||
|
39412c61bf | ||
|
ad4dde1d49 | ||
|
d51c574350 | ||
|
52d333f084 | ||
|
198e57bc37 | ||
|
ba97c0e98b | ||
|
c47f9a0a70 | ||
|
e97466b840 | ||
|
35d0045ce2 | ||
|
aca13f941c | ||
|
1ba4a7ec16 | ||
|
e9649b39ac | ||
|
ea95a9fa15 | ||
|
2ec50b9514 | ||
|
f587dc69bb | ||
|
d655cfe968 | ||
|
89b1b662b3 | ||
|
cf86abaf3c |
9
.air.conf
Normal file
9
.air.conf
Normal file
@@ -0,0 +1,9 @@
|
||||
root = "."
|
||||
tmp_dir = ".air"
|
||||
|
||||
[build]
|
||||
cmd = "make backend"
|
||||
bin = "gitea"
|
||||
include_ext = ["go", "tmpl"]
|
||||
exclude_dir = ["modules/git/tests", "services/gitdiff/testdata", "modules/avatar/testdata"]
|
||||
include_dir = ["cmd", "models", "modules", "options", "routers", "services", "templates"]
|
10
.air.toml
10
.air.toml
@@ -1,10 +0,0 @@
|
||||
root = "."
|
||||
tmp_dir = ".air"
|
||||
|
||||
[build]
|
||||
cmd = "make backend"
|
||||
bin = "gitea"
|
||||
include_ext = ["go", "tmpl"]
|
||||
exclude_dir = ["modules/git/tests", "services/gitdiff/testdata", "modules/avatar/testdata"]
|
||||
include_dir = ["cmd", "models", "modules", "options", "routers", "services", "templates"]
|
||||
exclude_regex = ["_test.go$"]
|
@@ -14,28 +14,28 @@ groups:
|
||||
name: BREAKING
|
||||
labels:
|
||||
- kind/breaking
|
||||
-
|
||||
name: SECURITY
|
||||
labels:
|
||||
- kind/security
|
||||
-
|
||||
name: FEATURES
|
||||
labels:
|
||||
- kind/feature
|
||||
-
|
||||
name: SECURITY
|
||||
labels:
|
||||
- kind/security
|
||||
-
|
||||
name: API
|
||||
labels:
|
||||
- kind/api
|
||||
-
|
||||
name: BUGFIXES
|
||||
labels:
|
||||
- kind/bug
|
||||
-
|
||||
name: ENHANCEMENTS
|
||||
labels:
|
||||
- kind/enhancement
|
||||
- kind/refactor
|
||||
- kind/ui
|
||||
-
|
||||
name: BUGFIXES
|
||||
labels:
|
||||
- kind/bug
|
||||
-
|
||||
name: TESTING
|
||||
labels:
|
||||
|
792
.drone.yml
792
.drone.yml
File diff suppressed because it is too large
Load Diff
@@ -12,15 +12,6 @@ insert_final_newline = true
|
||||
[*.{go,tmpl,html}]
|
||||
indent_style = tab
|
||||
|
||||
[templates/custom/*.tmpl]
|
||||
insert_final_newline = false
|
||||
|
||||
[templates/swagger/v1_json.tmpl]
|
||||
indent_style = space
|
||||
|
||||
[templates/user/auth/oidc_wellknown.tmpl]
|
||||
indent_style = space
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
|
84
.eslintrc
84
.eslintrc
@@ -6,16 +6,11 @@ ignorePatterns:
|
||||
|
||||
parserOptions:
|
||||
sourceType: module
|
||||
ecmaVersion: latest
|
||||
ecmaVersion: 2020
|
||||
|
||||
plugins:
|
||||
- eslint-plugin-unicorn
|
||||
- eslint-plugin-import
|
||||
- eslint-plugin-vue
|
||||
- eslint-plugin-html
|
||||
|
||||
extends:
|
||||
- plugin:vue/recommended
|
||||
|
||||
env:
|
||||
es2021: true
|
||||
@@ -26,20 +21,14 @@ globals:
|
||||
CodeMirror: false
|
||||
Dropzone: false
|
||||
SimpleMDE: false
|
||||
|
||||
settings:
|
||||
html/html-extensions: [".tmpl"]
|
||||
u2fApi: false
|
||||
|
||||
overrides:
|
||||
- files: ["web_src/**/*.js", "web_src/**/*.vue", "templates/**/*.tmpl"]
|
||||
- files: ["web_src/**/*.js"]
|
||||
env:
|
||||
browser: true
|
||||
jquery: true
|
||||
node: false
|
||||
- files: ["templates/**/*.tmpl"]
|
||||
rules:
|
||||
no-tabs: [0]
|
||||
indent: [2, tab, {SwitchCase: 1}]
|
||||
- files: ["web_src/**/*worker.js"]
|
||||
env:
|
||||
worker: true
|
||||
@@ -49,12 +38,6 @@ overrides:
|
||||
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]
|
||||
@@ -116,12 +99,11 @@ rules:
|
||||
import/no-amd: [0]
|
||||
import/no-anonymous-default-export: [0]
|
||||
import/no-commonjs: [0]
|
||||
import/no-cycle: [2, {ignoreExternal: true, maxDepth: 1}]
|
||||
import/no-cycle: [0]
|
||||
import/no-default-export: [0]
|
||||
import/no-deprecated: [0]
|
||||
import/no-dynamic-require: [0]
|
||||
import/no-extraneous-dependencies: [2]
|
||||
import/no-import-module-exports: [0]
|
||||
import/no-internal-modules: [0]
|
||||
import/no-mutable-exports: [2]
|
||||
import/no-named-as-default-member: [0]
|
||||
@@ -130,13 +112,12 @@ rules:
|
||||
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-unused-modules: [0]
|
||||
import/no-useless-path-segments: [2, {commonjs: true}]
|
||||
import/no-webpack-loader-syntax: [2]
|
||||
import/order: [0]
|
||||
@@ -230,7 +211,7 @@ rules:
|
||||
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-spaces: [2, {ignoreEOLComments: true, exceptions: {Property: true, VariableDeclarator: true}}]
|
||||
no-multi-str: [2]
|
||||
no-negated-condition: [0]
|
||||
no-nested-ternary: [0]
|
||||
@@ -239,7 +220,6 @@ rules:
|
||||
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]
|
||||
@@ -283,7 +263,6 @@ rules:
|
||||
no-unsafe-negation: [2]
|
||||
no-unused-expressions: [2]
|
||||
no-unused-labels: [2]
|
||||
no-unused-private-class-members: [2]
|
||||
no-unused-vars: [2, {args: all, argsIgnorePattern: ^_, varsIgnorePattern: ^_, caughtErrorsIgnorePattern: ^_, ignoreRestSiblings: false}]
|
||||
no-use-before-define: [2, nofunc]
|
||||
no-useless-backreference: [0]
|
||||
@@ -316,7 +295,6 @@ rules:
|
||||
prefer-exponentiation-operator: [2]
|
||||
prefer-named-capture-group: [0]
|
||||
prefer-numeric-literals: [2]
|
||||
prefer-object-has-own: [0]
|
||||
prefer-object-spread: [0]
|
||||
prefer-promise-reject-errors: [2, {allowEmptyReject: false}]
|
||||
prefer-regex-literals: [2]
|
||||
@@ -350,111 +328,61 @@ rules:
|
||||
unicode-bom: [2, never]
|
||||
unicorn/better-regex: [0]
|
||||
unicorn/catch-error-name: [0]
|
||||
unicorn/consistent-destructuring: [2]
|
||||
unicorn/consistent-function-scoping: [2]
|
||||
unicorn/custom-error-definition: [0]
|
||||
unicorn/empty-brace-spaces: [2]
|
||||
unicorn/error-message: [0]
|
||||
unicorn/escape-case: [0]
|
||||
unicorn/expiring-todo-comments: [0]
|
||||
unicorn/explicit-length-check: [0]
|
||||
unicorn/filename-case: [0]
|
||||
unicorn/import-index: [0]
|
||||
unicorn/import-style: [0]
|
||||
unicorn/new-for-builtins: [2]
|
||||
unicorn/no-abusive-eslint-disable: [0]
|
||||
unicorn/no-array-for-each: [2]
|
||||
unicorn/no-array-instanceof: [0]
|
||||
unicorn/no-array-method-this-argument: [2]
|
||||
unicorn/no-array-push-push: [2]
|
||||
unicorn/no-await-expression-member: [0]
|
||||
unicorn/no-console-spaces: [0]
|
||||
unicorn/no-document-cookie: [2]
|
||||
unicorn/no-empty-file: [2]
|
||||
unicorn/no-fn-reference-in-iterator: [0]
|
||||
unicorn/no-for-loop: [0]
|
||||
unicorn/no-hex-escape: [0]
|
||||
unicorn/no-invalid-remove-event-listener: [2]
|
||||
unicorn/no-keyword-prefix: [0]
|
||||
unicorn/no-lonely-if: [2]
|
||||
unicorn/no-nested-ternary: [0]
|
||||
unicorn/no-new-array: [0]
|
||||
unicorn/no-new-buffer: [0]
|
||||
unicorn/no-null: [0]
|
||||
unicorn/no-object-as-default-parameter: [2]
|
||||
unicorn/no-process-exit: [0]
|
||||
unicorn/no-reduce: [2]
|
||||
unicorn/no-static-only-class: [2]
|
||||
unicorn/no-thenable: [2]
|
||||
unicorn/no-this-assignment: [2]
|
||||
unicorn/no-unreadable-array-destructuring: [0]
|
||||
unicorn/no-unsafe-regex: [0]
|
||||
unicorn/no-unused-properties: [2]
|
||||
unicorn/no-useless-fallback-in-spread: [2]
|
||||
unicorn/no-useless-length-check: [2]
|
||||
unicorn/no-useless-promise-resolve-reject: [2]
|
||||
unicorn/no-useless-spread: [2]
|
||||
unicorn/no-useless-undefined: [0]
|
||||
unicorn/no-zero-fractions: [2]
|
||||
unicorn/number-literal-case: [0]
|
||||
unicorn/numeric-separators-style: [0]
|
||||
unicorn/prefer-add-event-listener: [2]
|
||||
unicorn/prefer-array-find: [2]
|
||||
unicorn/prefer-array-flat-map: [2]
|
||||
unicorn/prefer-array-flat: [2]
|
||||
unicorn/prefer-array-index-of: [2]
|
||||
unicorn/prefer-array-some: [2]
|
||||
unicorn/prefer-at: [0]
|
||||
unicorn/prefer-code-point: [2]
|
||||
unicorn/prefer-dataset: [2]
|
||||
unicorn/prefer-date-now: [2]
|
||||
unicorn/prefer-default-parameters: [0]
|
||||
unicorn/prefer-event-key: [2]
|
||||
unicorn/prefer-export-from: [2]
|
||||
unicorn/prefer-includes: [2]
|
||||
unicorn/prefer-json-parse-buffer: [0]
|
||||
unicorn/prefer-math-trunc: [2]
|
||||
unicorn/prefer-modern-dom-apis: [0]
|
||||
unicorn/prefer-module: [2]
|
||||
unicorn/prefer-negative-index: [2]
|
||||
unicorn/prefer-node-append: [0]
|
||||
unicorn/prefer-node-protocol: [0]
|
||||
unicorn/prefer-node-remove: [0]
|
||||
unicorn/prefer-number-properties: [0]
|
||||
unicorn/prefer-object-from-entries: [2]
|
||||
unicorn/prefer-object-has-own: [0]
|
||||
unicorn/prefer-optional-catch-binding: [2]
|
||||
unicorn/prefer-prototype-methods: [0]
|
||||
unicorn/prefer-query-selector: [0]
|
||||
unicorn/prefer-reflect-apply: [0]
|
||||
unicorn/prefer-regexp-test: [2]
|
||||
unicorn/prefer-replace-all: [0]
|
||||
unicorn/prefer-set-has: [0]
|
||||
unicorn/prefer-spread: [0]
|
||||
unicorn/prefer-starts-ends-with: [2]
|
||||
unicorn/prefer-string-slice: [0]
|
||||
unicorn/prefer-switch: [0]
|
||||
unicorn/prefer-ternary: [0]
|
||||
unicorn/prefer-text-content: [2]
|
||||
unicorn/prefer-top-level-await: [0]
|
||||
unicorn/prefer-trim-start-end: [2]
|
||||
unicorn/prefer-type-error: [0]
|
||||
unicorn/prevent-abbreviations: [0]
|
||||
unicorn/relative-url-style: [2]
|
||||
unicorn/require-array-join-separator: [2]
|
||||
unicorn/require-number-to-fixed-digits-argument: [2]
|
||||
unicorn/require-post-message-target-origin: [0]
|
||||
unicorn/string-content: [0]
|
||||
unicorn/template-indent: [2]
|
||||
unicorn/throw-new-error: [2]
|
||||
use-isnan: [2]
|
||||
valid-typeof: [2, {requireStringLiterals: true}]
|
||||
vars-on-top: [0]
|
||||
vue/attributes-order: [0]
|
||||
vue/component-definition-name-casing: [0]
|
||||
vue/html-closing-bracket-spacing: [0]
|
||||
vue/max-attributes-per-line: [0]
|
||||
vue/one-component-per-file: [0]
|
||||
wrap-iife: [2, inside]
|
||||
wrap-regex: [0]
|
||||
yield-star-spacing: [2, after]
|
||||
|
5
.gitattributes
vendored
5
.gitattributes
vendored
@@ -1,8 +1,3 @@
|
||||
* text=auto eol=lf
|
||||
/vendor/** -text -eol linguist-vendored
|
||||
/public/vendor/** -text -eol linguist-vendored
|
||||
/templates/**/*.tmpl linguist-language=Handlebars
|
||||
/.eslintrc linguist-language=YAML
|
||||
/.stylelintrc linguist-language=YAML
|
||||
/web_src/fomantic/build/** linguist-generated
|
||||
Dockerfile.* linguist-language=Dockerfile
|
||||
|
@@ -1,42 +0,0 @@
|
||||
<!-- NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue -->
|
||||
|
||||
<!--
|
||||
1. Please speak English, this is the language all maintainers can speak and write.
|
||||
2. Please ask questions or configuration/deploy problems on our Discord
|
||||
server (https://discord.gg/gitea) or forum (https://discourse.gitea.io).
|
||||
3. Please take a moment to check that your issue doesn't already exist.
|
||||
4. Make sure it's not mentioned in the FAQ (https://docs.gitea.io/en-us/faq)
|
||||
5. Please give all relevant information below for bug reports, because
|
||||
incomplete details will be handled as an invalid report.
|
||||
-->
|
||||
|
||||
- Gitea version (or commit ref):
|
||||
- Git version:
|
||||
- Operating system:
|
||||
<!-- Please include information on whether you built gitea yourself, used one of our downloads or are using some other package -->
|
||||
<!-- Please also tell us how you are running gitea, e.g. if it is being run from docker, a command-line, systemd etc. --->
|
||||
<!-- If you are using a package or systemd tell us what distribution you are using -->
|
||||
- Database (use `[x]`):
|
||||
- [ ] PostgreSQL
|
||||
- [ ] MySQL
|
||||
- [ ] MSSQL
|
||||
- [ ] SQLite
|
||||
- Can you reproduce the bug at https://try.gitea.io:
|
||||
- [ ] Yes (provide example URL)
|
||||
- [ ] No
|
||||
- Log gist:
|
||||
<!-- It really is important to provide pertinent logs -->
|
||||
<!-- Please read https://docs.gitea.io/en-us/logging-configuration/#debugging-problems -->
|
||||
<!-- In addition, if your problem relates to git commands set `RUN_MODE=dev` at the top of app.ini -->
|
||||
|
||||
## Description
|
||||
<!-- If using a proxy or a CDN (e.g. CloudFlare) in front of gitea, please
|
||||
disable the proxy/CDN fully and connect to gitea directly to confirm
|
||||
the issue still persists without those services. -->
|
||||
|
||||
...
|
||||
|
||||
|
||||
## Screenshots
|
||||
|
||||
<!-- **If this issue involves the Web Interface, please include a screenshot** -->
|
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@@ -1,2 +1 @@
|
||||
open_collective: gitea
|
||||
custom: https://www.bountysource.com/teams/gitea
|
||||
|
90
.github/ISSUE_TEMPLATE/bug-report.yaml
vendored
90
.github/ISSUE_TEMPLATE/bug-report.yaml
vendored
@@ -1,90 +0,0 @@
|
||||
name: Bug Report
|
||||
description: Found something you weren't expecting? Report it here!
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
1. Please speak English, this is the language all maintainers can speak and write.
|
||||
2. Please ask questions or configuration/deploy problems on our Discord
|
||||
server (https://discord.gg/gitea) or forum (https://discourse.gitea.io).
|
||||
3. Make sure you are using the latest release and
|
||||
take a moment to check that your issue hasn't been reported before.
|
||||
4. Make sure it's not mentioned in the FAQ (https://docs.gitea.io/en-us/faq)
|
||||
5. Please give all relevant information below for bug reports, because
|
||||
incomplete details will be handled as an invalid report.
|
||||
- type: input
|
||||
id: gitea-ver
|
||||
attributes:
|
||||
label: Gitea Version
|
||||
description: Gitea version (or commit reference) of your instance
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: git-ver
|
||||
attributes:
|
||||
label: Git Version
|
||||
description: The version of git running on the server
|
||||
- type: input
|
||||
id: os-ver
|
||||
attributes:
|
||||
label: Operating System
|
||||
description: The operating system you are using to run Gitea
|
||||
- type: textarea
|
||||
id: run-info
|
||||
attributes:
|
||||
label: How are you running Gitea?
|
||||
description: |
|
||||
Please include information on whether you built Gitea yourself, used one of our downloads, are using https://try.gitea.io or are using some other package
|
||||
Please also tell us how you are running Gitea, e.g. if it is being run from docker, a command-line, systemd etc.
|
||||
If you are using a package or systemd tell us what distribution you are using
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: database
|
||||
attributes:
|
||||
label: Database
|
||||
description: What database system are you running?
|
||||
options:
|
||||
- PostgreSQL
|
||||
- MySQL
|
||||
- MSSQL
|
||||
- SQLite
|
||||
- type: dropdown
|
||||
id: can-reproduce
|
||||
attributes:
|
||||
label: Can you reproduce the bug on the Gitea demo site?
|
||||
description: |
|
||||
If so, please provide a URL in the Description field
|
||||
URL of Gitea demo: https://try.gitea.io
|
||||
options:
|
||||
- "Yes"
|
||||
- "No"
|
||||
validations:
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
It's really important to provide pertinent logs
|
||||
Please read https://docs.gitea.io/en-us/logging-configuration/#debugging-problems
|
||||
In addition, if your problem relates to git commands set `RUN_MODE=dev` at the top of app.ini
|
||||
- type: input
|
||||
id: logs
|
||||
attributes:
|
||||
label: Log Gist
|
||||
description: Please provide a gist URL of your logs, with any sensitive information (e.g. API keys) removed/hidden
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: |
|
||||
Please provide a description of your issue here, with a URL if you were able to reproduce the issue (see above)
|
||||
If you are using a proxy or a CDN (e.g. Cloudflare) in front of Gitea, please disable the proxy/CDN fully and access Gitea directly to confirm the issue still persists without those services.
|
||||
- type: textarea
|
||||
id: screenshots
|
||||
attributes:
|
||||
label: Screenshots
|
||||
description: If this issue involves the Web Interface, please provide one or more screenshots
|
17
.github/ISSUE_TEMPLATE/config.yml
vendored
17
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,17 +0,0 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Security Concern
|
||||
url: https://tinyurl.com/security-gitea
|
||||
about: For security concerns, please send a mail to security@gitea.io instead of opening a public issue.
|
||||
- name: Discord Server
|
||||
url: https://discord.gg/gitea
|
||||
about: Please ask questions and discuss configuration or deployment problems here.
|
||||
- name: Discourse Forum
|
||||
url: https://discourse.gitea.io
|
||||
about: Questions and configuration or deployment problems can also be discussed on our forum.
|
||||
- name: Frequently Asked Questions
|
||||
url: https://docs.gitea.io/en-us/faq
|
||||
about: Please check if your question isn't mentioned here.
|
||||
- name: Crowdin Translations
|
||||
url: https://crowdin.com/project/gitea
|
||||
about: Translations are managed here.
|
23
.github/ISSUE_TEMPLATE/feature-request.yaml
vendored
23
.github/ISSUE_TEMPLATE/feature-request.yaml
vendored
@@ -1,23 +0,0 @@
|
||||
name: Feature Request
|
||||
description: Got an idea for a feature that Gitea doesn't have currently? Submit your idea here!
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
1. Please speak English, this is the language all maintainers can speak and write.
|
||||
2. Please ask questions or configuration/deploy problems on our Discord
|
||||
server (https://discord.gg/gitea) or forum (https://discourse.gitea.io).
|
||||
3. Please take a moment to check that your feature hasn't already been suggested.
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Feature Description
|
||||
placeholder: |
|
||||
I think it would be great if Gitea had...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: screenshots
|
||||
attributes:
|
||||
label: Screenshots
|
||||
description: If you can, provide screenshots of an implementation on another site e.g. GitHub
|
62
.github/ISSUE_TEMPLATE/ui.bug-report.yaml
vendored
62
.github/ISSUE_TEMPLATE/ui.bug-report.yaml
vendored
@@ -1,62 +0,0 @@
|
||||
name: Web Interface Bug Report
|
||||
description: Something doesn't look quite as it should? Report it here!
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
1. Please speak English, this is the language all maintainers can speak and write.
|
||||
2. Please ask questions or configuration/deploy problems on our Discord
|
||||
server (https://discord.gg/gitea) or forum (https://discourse.gitea.io).
|
||||
3. Please take a moment to check that your issue doesn't already exist.
|
||||
4. Make sure it's not mentioned in the FAQ (https://docs.gitea.io/en-us/faq)
|
||||
5. Please give all relevant information below for bug reports, because
|
||||
incomplete details will be handled as an invalid report.
|
||||
- type: input
|
||||
id: gitea-ver
|
||||
attributes:
|
||||
label: Gitea Version
|
||||
description: Gitea version (or commit reference) your instance is running
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: os-ver
|
||||
attributes:
|
||||
label: Operating System
|
||||
description: The operating system you are using to access Gitea
|
||||
- type: input
|
||||
id: browser-ver
|
||||
attributes:
|
||||
label: Browser Version
|
||||
description: The browser and version that you are using to access Gitea
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: can-reproduce
|
||||
attributes:
|
||||
label: Can you reproduce the bug on the Gitea demo site?
|
||||
description: |
|
||||
If so, please provide a URL in the Description field
|
||||
URL of Gitea demo: https://try.gitea.io
|
||||
options:
|
||||
- "Yes"
|
||||
- "No"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: |
|
||||
Please provide a description of your issue here, with a URL if you were able to reproduce the issue (see above)
|
||||
If using a proxy or a CDN (e.g. CloudFlare) in front of gitea, please disable the proxy/CDN fully and connect to gitea directly to confirm the issue still persists without those services.
|
||||
- type: textarea
|
||||
id: screenshots
|
||||
attributes:
|
||||
label: Screenshots
|
||||
description: Please provide at least 1 screenshot showing the issue.
|
||||
validations:
|
||||
required: true
|
33
.github/issue_template.md
vendored
Normal file
33
.github/issue_template.md
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<!-- NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue -->
|
||||
|
||||
<!--
|
||||
1. Please speak English, this is the language all maintainers can speak and write.
|
||||
2. Please ask questions or configuration/deploy problems on our Discord
|
||||
server (https://discord.gg/gitea) or forum (https://discourse.gitea.io).
|
||||
3. Please take a moment to check that your issue doesn't already exist.
|
||||
4. Please give all relevant information below for bug reports, because
|
||||
incomplete details will be handled as an invalid report.
|
||||
-->
|
||||
|
||||
- Gitea version (or commit ref):
|
||||
- Git version:
|
||||
- Operating system:
|
||||
- Database (use `[x]`):
|
||||
- [ ] PostgreSQL
|
||||
- [ ] MySQL
|
||||
- [ ] MSSQL
|
||||
- [ ] SQLite
|
||||
- Can you reproduce the bug at https://try.gitea.io:
|
||||
- [ ] Yes (provide example URL)
|
||||
- [ ] No
|
||||
- [ ] Not relevant
|
||||
- Log gist:
|
||||
|
||||
## Description
|
||||
|
||||
...
|
||||
|
||||
|
||||
## Screenshots
|
||||
|
||||
<!-- **If this issue involves the Web Interface, please include a screenshot** -->
|
23
.github/lock.yml
vendored
23
.github/lock.yml
vendored
@@ -1,23 +0,0 @@
|
||||
# Configuration for Lock Threads - https://github.com/dessant/lock-threads-app
|
||||
|
||||
# Number of days of inactivity before a closed issue or pull request is locked
|
||||
daysUntilLock: 60
|
||||
|
||||
# Skip issues and pull requests created before a given timestamp. Timestamp must
|
||||
# follow ISO 8601 (`YYYY-MM-DD`). `false` is disabled
|
||||
skipCreatedBefore: false
|
||||
|
||||
# Issues and pull requests with these labels will be ignored.
|
||||
exemptLabels: []
|
||||
|
||||
# Label to add before locking, such as `outdated`. `false` is disabled
|
||||
lockLabel: false
|
||||
|
||||
# Comment to post before locking.
|
||||
lockComment: >
|
||||
This thread has been automatically locked since there has not been
|
||||
any recent activity after it was closed. Please open a new issue for
|
||||
related bugs and link to relevant comments in this thread.
|
||||
|
||||
# Assign `resolved` as the reason for locking. Set to `false` to disable
|
||||
setLockReason: true
|
8
.github/pull_request_template.md
vendored
8
.github/pull_request_template.md
vendored
@@ -1,9 +1,7 @@
|
||||
<!--
|
||||
|
||||
Please check the following:
|
||||
|
||||
1. Make sure you are targeting the `main` branch, pull requests on release branches are only allowed for bug fixes.
|
||||
2. Read contributing guidelines: https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md
|
||||
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
|
||||
3. Describe what your pull request does and which issue you're targeting (if any)
|
||||
|
||||
-->
|
||||
**You MUST delete the content above including this line before posting, otherwise your pull request will be invalid.**
|
||||
|
22
.gitignore
vendored
22
.gitignore
vendored
@@ -9,8 +9,6 @@ _test
|
||||
|
||||
# IntelliJ
|
||||
.idea
|
||||
# Goland's output filename can not be set manually
|
||||
/go_build_*
|
||||
|
||||
# MS VSCode
|
||||
.vscode
|
||||
@@ -34,7 +32,6 @@ _testmain.go
|
||||
|
||||
*coverage.out
|
||||
coverage.all
|
||||
cpu.out
|
||||
|
||||
/modules/options/bindata.go
|
||||
/modules/options/bindata.go.hash
|
||||
@@ -78,26 +75,12 @@ cpu.out
|
||||
/integrations/mssql.ini
|
||||
/node_modules
|
||||
/yarn.lock
|
||||
/yarn-error.log
|
||||
/npm-debug.log*
|
||||
/public/js
|
||||
/public/serviceworker.js
|
||||
/public/css
|
||||
/public/fonts
|
||||
/public/img/webpack
|
||||
/vendor
|
||||
/web_src/fomantic/node_modules
|
||||
/web_src/fomantic/build/*
|
||||
!/web_src/fomantic/build/semantic.js
|
||||
!/web_src/fomantic/build/semantic.css
|
||||
!/web_src/fomantic/build/themes
|
||||
/web_src/fomantic/build/themes/*
|
||||
!/web_src/fomantic/build/themes/default
|
||||
/web_src/fomantic/build/themes/default/assets/*
|
||||
!/web_src/fomantic/build/themes/default/assets/fonts
|
||||
/web_src/fomantic/build/themes/default/assets/fonts/*
|
||||
!/web_src/fomantic/build/themes/default/assets/fonts/icons.woff2
|
||||
!/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
|
||||
/web_src/fomantic/build
|
||||
/VERSION
|
||||
/.air
|
||||
|
||||
@@ -113,6 +96,3 @@ prime/
|
||||
|
||||
# Make evidence files
|
||||
/.make_evidence
|
||||
|
||||
# Manpage
|
||||
/man
|
||||
|
@@ -9,14 +9,12 @@ linters:
|
||||
- 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
|
||||
- bidichk
|
||||
- ineffassign
|
||||
- revive
|
||||
enable-all: false
|
||||
disable-all: true
|
||||
fast: false
|
||||
@@ -28,35 +26,7 @@ linters-settings:
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
- ifElseChain
|
||||
- singleCaseSwitch # Every time this occurred in the code, there was no other way.
|
||||
revive:
|
||||
ignore-generated-header: false
|
||||
severity: warning
|
||||
confidence: 0.8
|
||||
errorCode: 1
|
||||
warningCode: 1
|
||||
rules:
|
||||
- name: blank-imports
|
||||
- name: context-as-argument
|
||||
- name: context-keys-type
|
||||
- name: dot-imports
|
||||
- name: error-return
|
||||
- name: error-strings
|
||||
- name: error-naming
|
||||
- name: exported
|
||||
- name: if-return
|
||||
- name: increment-decrement
|
||||
- name: var-naming
|
||||
- name: var-declaration
|
||||
- name: package-comments
|
||||
- name: range
|
||||
- name: receiver-naming
|
||||
- name: time-naming
|
||||
- name: unexported-return
|
||||
- name: indent-error-flow
|
||||
- name: errorf
|
||||
- name: duplicated-imports
|
||||
- name: modifies-value-receiver
|
||||
- singleCaseSwitch # Every time this occured in the code, there was no other way.
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
@@ -100,6 +70,9 @@ issues:
|
||||
- path: modules/log/
|
||||
linters:
|
||||
- errcheck
|
||||
- path: routers/routes/routes.go
|
||||
linters:
|
||||
- dupl
|
||||
- path: routers/api/v1/repo/issue_subscription.go
|
||||
linters:
|
||||
- dupl
|
||||
@@ -128,19 +101,9 @@ issues:
|
||||
- path: cmd/dump.go
|
||||
linters:
|
||||
- dupl
|
||||
- path: services/webhook/webhook.go
|
||||
linters:
|
||||
- structcheck
|
||||
- text: "commentFormatting: put a space between `//` and comment text"
|
||||
linters:
|
||||
- gocritic
|
||||
- text: "exitAfterDefer:"
|
||||
linters:
|
||||
- gocritic
|
||||
- path: modules/graceful/manager_windows.go
|
||||
linters:
|
||||
- staticcheck
|
||||
text: "svc.IsAnInteractiveSession is deprecated: Use IsWindowsService instead."
|
||||
- path: models/user/openid.go
|
||||
linters:
|
||||
- golint
|
||||
|
7
.ignore
7
.ignore
@@ -1,8 +1,5 @@
|
||||
*.min.css
|
||||
*.min.js
|
||||
/vendor
|
||||
/public/vendor/plugins
|
||||
/modules/options/bindata.go
|
||||
/modules/public/bindata.go
|
||||
/modules/templates/bindata.go
|
||||
/public/vendor/plugins
|
||||
/vendor
|
||||
node_modules
|
||||
|
3
.npmrc
3
.npmrc
@@ -1,5 +1,2 @@
|
||||
audit=false
|
||||
fund=false
|
||||
update-notifier=false
|
||||
package-lock=true
|
||||
save-exact=true
|
||||
|
25
.revive.toml
Normal file
25
.revive.toml
Normal file
@@ -0,0 +1,25 @@
|
||||
ignoreGeneratedHeader = false
|
||||
severity = "warning"
|
||||
confidence = 0.8
|
||||
errorCode = 1
|
||||
warningCode = 1
|
||||
|
||||
[rule.blank-imports]
|
||||
[rule.context-as-argument]
|
||||
[rule.context-keys-type]
|
||||
[rule.dot-imports]
|
||||
[rule.error-return]
|
||||
[rule.error-strings]
|
||||
[rule.error-naming]
|
||||
[rule.exported]
|
||||
[rule.if-return]
|
||||
[rule.increment-decrement]
|
||||
[rule.var-naming]
|
||||
[rule.var-declaration]
|
||||
[rule.package-comments]
|
||||
[rule.range]
|
||||
[rule.receiver-naming]
|
||||
[rule.time-naming]
|
||||
[rule.unexported-return]
|
||||
[rule.indent-error-flow]
|
||||
[rule.errorf]
|
18
.stylelintrc
18
.stylelintrc
@@ -1,31 +1,15 @@
|
||||
extends: stylelint-config-standard
|
||||
|
||||
overrides:
|
||||
- files: ["**/*.less"]
|
||||
customSyntax: postcss-less
|
||||
|
||||
rules:
|
||||
alpha-value-notation: null
|
||||
at-rule-empty-line-before: null
|
||||
block-closing-brace-empty-line-before: null
|
||||
color-function-notation: null
|
||||
color-hex-length: null
|
||||
comment-empty-line-before: null
|
||||
declaration-block-no-redundant-longhand-properties: null
|
||||
declaration-block-single-line-max-declarations: null
|
||||
declaration-empty-line-before: null
|
||||
hue-degree-notation: null
|
||||
indentation: 2
|
||||
max-line-length: null
|
||||
no-descending-specificity: null
|
||||
no-invalid-position-at-import-rule: null
|
||||
number-leading-zero: never
|
||||
number-max-precision: null
|
||||
property-no-vendor-prefix: null
|
||||
rule-empty-line-before: null
|
||||
selector-class-pattern: null
|
||||
selector-id-pattern: null
|
||||
selector-pseudo-element-colon-notation: double
|
||||
selector-pseudo-element-colon-notation: null
|
||||
shorthand-property-no-redundant-values: true
|
||||
string-quotes: null
|
||||
value-no-vendor-prefix: null
|
||||
|
1296
CHANGELOG.md
1296
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -3,14 +3,12 @@
|
||||
## Table of Contents
|
||||
|
||||
- [Contribution Guidelines](#contribution-guidelines)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Introduction](#introduction)
|
||||
- [Bug reports](#bug-reports)
|
||||
- [Discuss your design](#discuss-your-design)
|
||||
- [Testing redux](#testing-redux)
|
||||
- [Vendoring](#vendoring)
|
||||
- [Translation](#translation)
|
||||
- [Building Gitea](#building-gitea)
|
||||
- [Code review](#code-review)
|
||||
- [Styleguide](#styleguide)
|
||||
- [Design guideline](#design-guideline)
|
||||
@@ -81,22 +79,23 @@ Here's how to run the test suite:
|
||||
|``make lint-frontend`` | lint frontend files |
|
||||
|``make lint-backend`` | lint backend files |
|
||||
|
||||
- run test code (Suggest run in Linux)
|
||||
- run test code (Suggest run in linux)
|
||||
|
||||
| | |
|
||||
| :------------------------------------- | :----------------------------------------------- |
|
||||
|``make test[\#TestSpecificName]`` | run unit test |
|
||||
|``make test-sqlite[\#TestSpecificName]``| run [integration](integrations) test for SQLite |
|
||||
|[More details about integrations](integrations/README.md) |
|
||||
|``make test-sqlite[\#TestSpecificName]``| run [integration](integrations) test for sqlite |
|
||||
|[More detail message about integrations](integrations/README.md) |
|
||||
|
||||
## Vendoring
|
||||
|
||||
We manage dependencies via [Go Modules](https://golang.org/cmd/go/#hdr-Module_maintenance), more details: [go mod](https://go.dev/ref/mod).
|
||||
We keep a cached copy of dependencies within the `vendor/` directory,
|
||||
managing updates via [Modules](https://golang.org/cmd/go/#hdr-Module_maintenance).
|
||||
|
||||
Pull requests should only include `go.mod`, `go.sum` updates if they are part of
|
||||
Pull requests should only include `vendor/` updates if they are part of
|
||||
the same change, be it a bugfix or a feature addition.
|
||||
|
||||
The `go.mod`, `go.sum` update needs to be justified as part of the PR description,
|
||||
The `vendor/` update needs to be justified as part of the PR description,
|
||||
and must be verified by the reviewers and/or merger to always reference
|
||||
an existing upstream commit.
|
||||
|
||||
@@ -105,7 +104,7 @@ You can find more information on how to get started with it on the [Modules Wiki
|
||||
## Translation
|
||||
|
||||
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)
|
||||
and is synced regularly to Crowdin. Once a translation has reached
|
||||
A SATISFACTORY PERCENTAGE it will be synced back into this repo and
|
||||
@@ -156,10 +155,10 @@ import (
|
||||
|
||||
## Design guideline
|
||||
|
||||
To maintain understandable code and avoid circular dependencies it is important to have a good structure of the code. The Gitea code is divided into the following parts:
|
||||
To maintain understandable code and avoid circular dependencies it is important to have a good structure of the code. The gitea code is divided into the following parts:
|
||||
|
||||
- **integration:** Integrations tests
|
||||
- **models:** Contains the data structures used by xorm to construct database tables. It also contains supporting functions to query and update the database. Dependencies to other code in Gitea should be avoided although some modules might be needed (for example for logging).
|
||||
- **models:** Contains the data structures used by xorm to construct database tables. It also contains supporting functions to query and update the database. Dependecies to other code in Gitea should be avoided although some modules might be needed (for example for logging).
|
||||
- **models/fixtures:** Sample model data used in integration tests.
|
||||
- **models/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.
|
||||
@@ -182,16 +181,16 @@ The same applies to status responses. If you notice a problem, feel free to leav
|
||||
All expected results (errors, success, fail messages) should be documented
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L319-L327)).
|
||||
|
||||
All JSON input types must be defined as a struct in [modules/structs/](modules/structs/)
|
||||
All JSON input types must be defined as a struct in `models/structs/`
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/modules/structs/issue.go#L76-L91))
|
||||
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/)
|
||||
All JSON responses must be defined as a struct in `models/structs/`
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/modules/structs/issue.go#L36-L68))
|
||||
and referenced in its category in [routers/api/v1/swagger/](routers/api/v1/swagger/)
|
||||
and referenced in its category in `routers/api/v1/swagger/`
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/swagger/issue.go#L11-L16))
|
||||
They can be used like the following:
|
||||
([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L277-L279))
|
||||
@@ -200,16 +199,12 @@ In general, HTTP methods are chosen as follows:
|
||||
* **GET** endpoints return requested object and status **OK (200)**
|
||||
* **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)
|
||||
* **PUT** endpoints return status **No Content (204)**, used to **add/assign** existing Obejcts (e.g. User) to something (e.g. Org-Team)
|
||||
* **PATCH** endpoints return changed object and status **OK (200)**, used to **edit/change** an existing object
|
||||
|
||||
|
||||
An endpoint which changes/edits an object expects all fields to be optional (except ones to identify the object, which are required).
|
||||
|
||||
### 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)
|
||||
|
||||
@@ -222,7 +217,7 @@ Additionally you could add a line at the end of your commit message.
|
||||
Signed-off-by: Joe Smith <joe.smith@email.com>
|
||||
```
|
||||
|
||||
If you set your `user.name` and `user.email` Git configs, you can add the
|
||||
If you set your `user.name` and `user.email` git configs, you can add the
|
||||
line to the end of your commit automatically with `git commit -s`.
|
||||
|
||||
We assume in good faith that the information you provide is legally binding.
|
||||
@@ -231,18 +226,18 @@ We assume in good faith that the information you provide is legally binding.
|
||||
|
||||
We adopted a release schedule to streamline the process of working
|
||||
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
|
||||
known as the release freeze. All the feature pull requests should be
|
||||
merged before feature freeze. And, during the frozen period, a corresponding
|
||||
release branch is open for fixes backported from main branch. Release candidates
|
||||
are made during this period for user testing to
|
||||
merged in the first month of one release period. And, during the frozen
|
||||
period, a corresponding release branch is open for fixes backported from
|
||||
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
|
||||
maintained by issuing patch releases to only correct critical problems
|
||||
such as crashes or security issues.
|
||||
|
||||
Major release cycles are seasonal. They always begin on the 25th and end on
|
||||
the 24th (i.e., the 25th of December to March 24th).
|
||||
Major release cycles are bimonthly. They always begin on the 25th and end on
|
||||
the 24th (i.e., the 25th of December to February 24th).
|
||||
|
||||
During a development cycle, we may also publish any necessary minor releases
|
||||
for the previous version. For example, if the latest, published release is
|
||||
@@ -267,7 +262,7 @@ to the maintainers team. If a maintainer is inactive for more than 3
|
||||
months and forgets to leave the maintainers team, the owners may move
|
||||
him or her from the maintainers team to the advisors team.
|
||||
For security reasons, Maintainers should use 2FA for their accounts and
|
||||
if possible provide GPG signed commits.
|
||||
if possible provide gpg signed commits.
|
||||
https://help.github.com/articles/securing-your-account-with-two-factor-authentication-2fa/
|
||||
https://help.github.com/articles/signing-commits-with-gpg/
|
||||
|
||||
@@ -298,16 +293,6 @@ and lead the development of Gitea.
|
||||
To honor the past owners, here's the history of the owners and the time
|
||||
they served:
|
||||
|
||||
* 2022-01-01 ~ 2022-12-31 - https://github.com/go-gitea/gitea/issues/17872
|
||||
* [Lunny Xiao](https://gitea.com/lunny) <xiaolunwen@gmail.com>
|
||||
* [Matti Ranta](https://gitea.com/techknowlogick) <techknowlogick@gitea.io>
|
||||
* [Andrew Thornton](https://gitea.com/zeripath) <art27@cantab.net>
|
||||
|
||||
* 2021-01-01 ~ 2021-12-31 - https://github.com/go-gitea/gitea/issues/13801
|
||||
* [Lunny Xiao](https://gitea.com/lunny) <xiaolunwen@gmail.com>
|
||||
* [Lauris Bukšis-Haberkorns](https://gitea.com/lafriks) <lauris@nix.lv>
|
||||
* [Matti Ranta](https://gitea.com/techknowlogick) <techknowlogick@gitea.io>
|
||||
|
||||
* 2020-01-01 ~ 2020-12-31 - https://github.com/go-gitea/gitea/issues/9230
|
||||
* [Lunny Xiao](https://gitea.com/lunny) <xiaolunwen@gmail.com>
|
||||
* [Lauris Bukšis-Haberkorns](https://gitea.com/lafriks) <lauris@nix.lv>
|
||||
@@ -330,13 +315,13 @@ they served:
|
||||
|
||||
## Versions
|
||||
|
||||
Gitea has the `main` branch as a tip branch and has version branches
|
||||
Gitea has the `master` branch as a tip branch and has version branches
|
||||
such as `release/v0.9`. `release/v0.9` is a release branch and we will
|
||||
tag `v0.9.0` for binary download. If `v0.9.0` has bugs, we will accept
|
||||
pull requests on the `release/v0.9` branch and publish a `v0.9.1` tag,
|
||||
after bringing the bug fix also to the main branch.
|
||||
after bringing the bug fix also to the master branch.
|
||||
|
||||
Since the `main` branch is a tip version, if you wish to use Gitea
|
||||
Since the `master` branch is a tip version, if you wish to use Gitea
|
||||
in production, please download the latest release tag version. All the
|
||||
branches will be protected via GitHub, all the PRs to every branch must
|
||||
be reviewed by two maintainers and must pass the automatic tests.
|
||||
@@ -344,14 +329,14 @@ be reviewed by two maintainers and must pass the automatic tests.
|
||||
## Releasing Gitea
|
||||
|
||||
* 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 `main` with PRs with label `changelog` and after it has been merged do following steps:
|
||||
* 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 create a release and upload all the compiled binary. (But currently it doesn't add the release notes automatically. Maybe we should fix that.)
|
||||
* If needed send PR for changelog on branch `main`.
|
||||
* 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
|
||||
|
15
Dockerfile
15
Dockerfile
@@ -1,7 +1,7 @@
|
||||
|
||||
###################################
|
||||
#Build stage - temporarily using techknowlogick image until we upgrade to latest official alpine/go image
|
||||
FROM techknowlogick/go:1.17-alpine3.13 AS build-env
|
||||
#Build stage
|
||||
FROM golang:1.15-alpine3.12 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY ${GOPROXY:-direct}
|
||||
@@ -22,10 +22,7 @@ WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
||||
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
|
||||
FROM alpine:3.12
|
||||
LABEL maintainer="maintainers@gitea.io"
|
||||
|
||||
EXPOSE 22 3000
|
||||
@@ -53,7 +50,7 @@ RUN addgroup \
|
||||
-u 1000 \
|
||||
-G git \
|
||||
git && \
|
||||
echo "git:*" | chpasswd -e
|
||||
echo "git:$(dd if=/dev/urandom bs=24 count=1 status=none | base64)" | chpasswd
|
||||
|
||||
ENV USER git
|
||||
ENV GITEA_CUSTOM /data/gitea
|
||||
@@ -65,6 +62,4 @@ CMD ["/bin/s6-svscan", "/etc/s6"]
|
||||
|
||||
COPY docker/root /
|
||||
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
||||
COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
||||
RUN chmod 755 /usr/bin/entrypoint /app/gitea/gitea /usr/local/bin/gitea /usr/local/bin/environment-to-ini
|
||||
RUN chmod 755 /etc/s6/gitea/* /etc/s6/openssh/* /etc/s6/.s6-svscan/*
|
||||
RUN ln -s /app/gitea/gitea /usr/local/bin/gitea
|
||||
|
@@ -1,75 +0,0 @@
|
||||
|
||||
###################################
|
||||
#Build stage - temporarily using techknowlogick image until we upgrade to latest official alpine/go image
|
||||
FROM techknowlogick/go:1.17-alpine3.13 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY ${GOPROXY:-direct}
|
||||
|
||||
ARG GITEA_VERSION
|
||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
||||
ENV TAGS "bindata timetzdata $TAGS"
|
||||
ARG CGO_EXTRA_CFLAGS
|
||||
|
||||
#Build deps
|
||||
RUN apk --no-cache add build-base git nodejs npm
|
||||
|
||||
#Setup repo
|
||||
COPY . ${GOPATH}/src/code.gitea.io/gitea
|
||||
WORKDIR ${GOPATH}/src/code.gitea.io/gitea
|
||||
|
||||
#Checkout version if set
|
||||
RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
||||
&& make clean-all build
|
||||
|
||||
# Begin env-to-ini build
|
||||
RUN go build contrib/environment-to-ini/environment-to-ini.go
|
||||
|
||||
FROM alpine:3.13
|
||||
LABEL maintainer="maintainers@gitea.io"
|
||||
|
||||
EXPOSE 2222 3000
|
||||
|
||||
RUN apk --no-cache add \
|
||||
bash \
|
||||
ca-certificates \
|
||||
gettext \
|
||||
git \
|
||||
curl \
|
||||
gnupg
|
||||
|
||||
RUN addgroup \
|
||||
-S -g 1000 \
|
||||
git && \
|
||||
adduser \
|
||||
-S -H -D \
|
||||
-h /var/lib/gitea/git \
|
||||
-s /bin/bash \
|
||||
-u 1000 \
|
||||
-G git \
|
||||
git
|
||||
|
||||
RUN mkdir -p /var/lib/gitea /etc/gitea
|
||||
RUN chown git:git /var/lib/gitea /etc/gitea
|
||||
|
||||
COPY docker/rootless /
|
||||
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
||||
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
||||
RUN chmod 755 /usr/local/bin/docker-entrypoint.sh /usr/local/bin/docker-setup.sh /app/gitea/gitea /usr/local/bin/gitea /usr/local/bin/environment-to-ini
|
||||
|
||||
#git:git
|
||||
USER 1000:1000
|
||||
ENV GITEA_WORK_DIR /var/lib/gitea
|
||||
ENV GITEA_CUSTOM /var/lib/gitea/custom
|
||||
ENV GITEA_TEMP /tmp/gitea
|
||||
ENV TMPDIR /tmp/gitea
|
||||
|
||||
#TODO add to docs the ability to define the ini to load (usefull to test and revert a config)
|
||||
ENV GITEA_APP_INI /etc/gitea/app.ini
|
||||
ENV HOME "/var/lib/gitea/git"
|
||||
VOLUME ["/var/lib/gitea", "/etc/gitea"]
|
||||
WORKDIR /var/lib/gitea
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
||||
CMD []
|
||||
|
13
MAINTAINERS
13
MAINTAINERS
@@ -1,4 +1,5 @@
|
||||
Alexey Makhov <amakhov@avito.ru> (@makhov)
|
||||
Andrey Nering <andrey.nering@gmail.com> (@andreynering)
|
||||
Bo-Yi Wu <appleboy.tw@gmail.com> (@appleboy)
|
||||
Ethan Koenig <ethantkoenig@gmail.com> (@ethantkoenig)
|
||||
Kees de Vries <bouwko@gmail.com> (@Bwko)
|
||||
@@ -35,14 +36,4 @@ 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)
|
||||
Leon Hofmeister <dev.lh@web.de> (@delvh)
|
||||
Gusted <williamzijl7@hotmail.com) (@Gusted)
|
||||
singuliere <singuliere@autistici.org> (@singuliere)
|
||||
CirnoT <gitea.m@i32.pl> (@CirnoT)
|
||||
|
355
Makefile
355
Makefile
@@ -1,3 +1,4 @@
|
||||
|
||||
ifeq ($(USE_REPO_TEST_DIR),1)
|
||||
|
||||
# This rule replaces the whole Makefile when we're trying to use /tmp repository temporary files
|
||||
@@ -24,10 +25,9 @@ SHASUM ?= shasum -a 256
|
||||
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
|
||||
MIN_GOLANGCI_LINT_VERSION := 001043000
|
||||
XGO_VERSION := go-1.15.x
|
||||
MIN_GO_VERSION := 001013000
|
||||
MIN_NODE_VERSION := 010013000
|
||||
|
||||
DOCKER_IMAGE ?= gitea/gitea
|
||||
DOCKER_TAG ?= latest
|
||||
@@ -44,9 +44,6 @@ endif
|
||||
ifeq ($(OS), Windows_NT)
|
||||
GOFLAGS := -v -buildmode=exe
|
||||
EXECUTABLE ?= gitea.exe
|
||||
else ifeq ($(OS), Windows)
|
||||
GOFLAGS := -v -buildmode=exe
|
||||
EXECUTABLE ?= gitea.exe
|
||||
else
|
||||
GOFLAGS := -v
|
||||
EXECUTABLE ?= gitea
|
||||
@@ -58,14 +55,15 @@ else
|
||||
SED_INPLACE := sed -i ''
|
||||
endif
|
||||
|
||||
GOFMT ?= gofmt -s
|
||||
|
||||
EXTRA_GOFLAGS ?=
|
||||
|
||||
MAKE_VERSION := $(shell $(MAKE) -v | head -n 1)
|
||||
MAKE_EVIDENCE_DIR := .make_evidence
|
||||
|
||||
ifeq ($(RACE_ENABLED),true)
|
||||
GOFLAGS += -race
|
||||
GOTESTFLAGS += -race
|
||||
ifneq ($(RACE_ENABLED),)
|
||||
GOTESTFLAGS ?= -race
|
||||
endif
|
||||
|
||||
STORED_VERSION_FILE := VERSION
|
||||
@@ -77,7 +75,7 @@ else
|
||||
ifneq ($(DRONE_BRANCH),)
|
||||
VERSION ?= $(subst release/v,,$(DRONE_BRANCH))
|
||||
else
|
||||
VERSION ?= main
|
||||
VERSION ?= master
|
||||
endif
|
||||
|
||||
STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null)
|
||||
@@ -90,13 +88,13 @@ 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/integrations/migration-test,$(filter-out code.gitea.io/gitea/integrations,$(shell $(GO) list -mod=vendor ./... | grep -v /vendor/)))
|
||||
|
||||
GO_PACKAGES ?= $(filter-out code.gitea.io/gitea/models/migrations code.gitea.io/gitea/integrations/migration-test code.gitea.io/gitea/integrations,$(shell $(GO) list ./... | grep -v /vendor/))
|
||||
FOMANTIC_CONFIGS := semantic.json web_src/fomantic/theme.config.less web_src/fomantic/_site/globals/site.variables
|
||||
FOMANTIC_DEST := web_src/fomantic/build/semantic.js web_src/fomantic/build/semantic.css
|
||||
FOMANTIC_DEST_DIR := web_src/fomantic/build
|
||||
|
||||
FOMANTIC_WORK_DIR := web_src/fomantic
|
||||
|
||||
WEBPACK_SOURCES := $(shell find web_src/js web_src/less -type f)
|
||||
WEBPACK_SOURCES := $(shell find web_src/js web_src/less -type f) $(FOMANTIC_DEST)
|
||||
WEBPACK_CONFIGS := webpack.config.js
|
||||
WEBPACK_DEST := public/js/index.js public/css/index.css
|
||||
WEBPACK_DEST_ENTRIES := public/js public/css public/fonts public/img/webpack public/serviceworker.js
|
||||
@@ -112,12 +110,7 @@ TAGS ?=
|
||||
TAGS_SPLIT := $(subst $(COMMA), ,$(TAGS))
|
||||
TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
|
||||
|
||||
TEST_TAGS ?= sqlite sqlite_unlock_notify
|
||||
|
||||
TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMANTIC_WORK_DIR)/node_modules $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR)
|
||||
|
||||
GO_DIRS := cmd integrations models modules routers build services tools
|
||||
|
||||
GO_DIRS := cmd integrations models modules routers build services vendor
|
||||
GO_SOURCES := $(wildcard *.go)
|
||||
GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" -not -path modules/options/bindata.go -not -path modules/public/bindata.go -not -path modules/templates/bindata.go)
|
||||
|
||||
@@ -125,11 +118,13 @@ 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 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_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl}}/api/v1"|g
|
||||
SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl}}/api/v1"|"basePath": "/api/v1"|g
|
||||
SWAGGER_EXCLUDE := code.gitea.io/sdk
|
||||
SWAGGER_NEWLINE_COMMAND := -e '$$a\'
|
||||
|
||||
@@ -157,7 +152,7 @@ all: build
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo "Make Routines:"
|
||||
@echo " - \"\" equivalent to \"build\""
|
||||
@echo " - \"\" equivalent to \"build\""
|
||||
@echo " - build build everything"
|
||||
@echo " - frontend build frontend files"
|
||||
@echo " - backend build backend files"
|
||||
@@ -172,9 +167,6 @@ help:
|
||||
@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"
|
||||
@@ -182,20 +174,21 @@ help:
|
||||
@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[\#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 '.' ' ');))
|
||||
$(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/"; \
|
||||
echo "Gitea requires Go 1.13 or greater to build. You can get it at https://golang.org/dl/"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
@@ -209,16 +202,15 @@ git-check:
|
||||
.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/"; \
|
||||
echo "Gitea requires Node.js 10 or greater and npm to build. You can get it at https://nodejs.org/en/download/"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: clean-all
|
||||
clean-all: clean
|
||||
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules
|
||||
rm -rf $(WEBPACK_DEST_ENTRIES) $(FOMANTIC_DEST_DIR)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@@ -227,18 +219,19 @@ clean:
|
||||
integrations*.test \
|
||||
integrations/gitea-integration-pgsql/ integrations/gitea-integration-mysql/ integrations/gitea-integration-mysql8/ integrations/gitea-integration-sqlite/ \
|
||||
integrations/gitea-integration-mssql/ integrations/indexers-mysql/ integrations/indexers-mysql8/ integrations/indexers-pgsql integrations/indexers-sqlite \
|
||||
integrations/indexers-mssql integrations/mysql.ini integrations/mysql8.ini integrations/pgsql.ini integrations/mssql.ini man/
|
||||
integrations/indexers-mssql integrations/mysql.ini integrations/mysql8.ini integrations/pgsql.ini integrations/mssql.ini
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
@echo "Running gitea-fmt(with gofmt)..."
|
||||
@$(GO) run build/code-batch-process.go gitea-fmt -s -w '{file-list}'
|
||||
$(GOFMT) -w $(GO_SOURCES_OWN)
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
@echo "Running go vet..."
|
||||
@GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet
|
||||
@$(GO) vet -vettool=gitea-vet $(GO_PACKAGES)
|
||||
# Default vet
|
||||
$(GO) vet $(GO_PACKAGES)
|
||||
# Custom vet
|
||||
$(GO) build -mod=vendor code.gitea.io/gitea-vet
|
||||
$(GO) vet -vettool=gitea-vet $(GO_PACKAGES)
|
||||
|
||||
.PHONY: $(TAGS_EVIDENCE)
|
||||
$(TAGS_EVIDENCE):
|
||||
@@ -262,7 +255,7 @@ swagger-check: generate-swagger
|
||||
echo "Please run 'make generate-swagger' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
fi;
|
||||
|
||||
.PHONY: swagger-validate
|
||||
swagger-validate:
|
||||
@@ -273,111 +266,114 @@ swagger-validate:
|
||||
.PHONY: errcheck
|
||||
errcheck:
|
||||
@hash errcheck > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install github.com/kisielk/errcheck@8ddee489636a8311a376fc92e27a6a13c6658344; \
|
||||
GO111MODULE=off $(GO) get -u github.com/kisielk/errcheck; \
|
||||
fi
|
||||
@echo "Running errcheck..."
|
||||
@errcheck $(GO_PACKAGES)
|
||||
errcheck $(GO_PACKAGES)
|
||||
|
||||
.PHONY: revive
|
||||
revive:
|
||||
GO111MODULE=on $(GO) run -mod=vendor build/lint.go -config .revive.toml -exclude=./vendor/... ./... || exit 1
|
||||
|
||||
.PHONY: misspell-check
|
||||
misspell-check:
|
||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
||||
fi
|
||||
misspell -error -i unknwon,destory $(GO_SOURCES_OWN)
|
||||
|
||||
.PHONY: misspell
|
||||
misspell:
|
||||
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u github.com/client9/misspell/cmd/misspell; \
|
||||
fi
|
||||
misspell -w -i unknwon $(GO_SOURCES_OWN)
|
||||
|
||||
.PHONY: fmt-check
|
||||
fmt-check:
|
||||
# get all go files and run gitea-fmt (with gofmt) on them
|
||||
@diff=$$($(GO) run build/code-batch-process.go gitea-fmt -s -d '{file-list}'); \
|
||||
# get all go files and run go fmt on them
|
||||
@diff=$$($(GOFMT) -d $(GO_SOURCES_OWN)); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make fmt' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
fi;
|
||||
|
||||
.PHONY: checks
|
||||
checks: checks-frontend checks-backend
|
||||
|
||||
.PHONY: checks-frontend
|
||||
checks-frontend: lockfile-check svg-check
|
||||
checks-frontend: svg-check
|
||||
|
||||
.PHONY: checks-backend
|
||||
checks-backend: gomod-check swagger-check swagger-validate
|
||||
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 docs/assets/js
|
||||
npx stylelint --color --max-warnings=0 web_src/less
|
||||
npx editorconfig-checker templates
|
||||
npx eslint web_src/js build webpack.config.js
|
||||
npx stylelint web_src/less
|
||||
|
||||
.PHONY: lint-backend
|
||||
lint-backend: golangci-lint vet
|
||||
lint-backend: golangci-lint revive vet
|
||||
|
||||
.PHONY: watch
|
||||
watch:
|
||||
bash tools/watch.sh
|
||||
|
||||
.PHONY: watch-frontend
|
||||
watch-frontend: node-check node_modules
|
||||
watch-frontend: node-check $(FOMANTIC_DEST) node_modules
|
||||
rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||
NODE_ENV=development npx webpack --watch --progress
|
||||
NODE_ENV=development npx webpack --hide-modules --display-entrypoints=false --watch --progress
|
||||
|
||||
.PHONY: watch-backend
|
||||
watch-backend: go-check
|
||||
@hash air > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install github.com/cosmtrek/air@bedc18201271882c2be66d216d0e1a275b526ec4; \
|
||||
GO111MODULE=off $(GO) get -u github.com/cosmtrek/air; \
|
||||
fi
|
||||
air -c .air.toml
|
||||
air -c .air.conf
|
||||
|
||||
.PHONY: test
|
||||
test: test-frontend test-backend
|
||||
|
||||
.PHONY: test-backend
|
||||
test-backend:
|
||||
@echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
|
||||
@$(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_PACKAGES)
|
||||
|
||||
.PHONY: test-frontend
|
||||
test-frontend: node_modules
|
||||
@NODE_OPTIONS="--experimental-vm-modules --no-warnings" npx jest --color
|
||||
test:
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -tags='sqlite sqlite_unlock_notify' $(GO_PACKAGES)
|
||||
|
||||
.PHONY: test-check
|
||||
test-check:
|
||||
@echo "Running test-check...";
|
||||
@echo "Checking if tests have changed the source tree...";
|
||||
@diff=$$(git status -s); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "make test-backend has changed files in the source tree:"; \
|
||||
echo "make test has changed files in the source tree:"; \
|
||||
echo "$${diff}"; \
|
||||
echo "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
|
||||
fi;
|
||||
|
||||
.PHONY: test\#%
|
||||
test\#%:
|
||||
@echo "Running go test with -tags '$(TEST_TAGS)'..."
|
||||
@$(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' -run $(subst .,/,$*) $(GO_PACKAGES)
|
||||
$(GO) test -mod=vendor -tags='sqlite sqlite_unlock_notify' -run $(subst .,/,$*) $(GO_PACKAGES)
|
||||
|
||||
.PHONY: coverage
|
||||
coverage:
|
||||
grep '^\(mode: .*\)\|\(.*:[0-9]\+\.[0-9]\+,[0-9]\+\.[0-9]\+ [0-9]\+ [0-9]\+\)$$' coverage.out > coverage-bodged.out
|
||||
grep '^\(mode: .*\)\|\(.*:[0-9]\+\.[0-9]\+,[0-9]\+\.[0-9]\+ [0-9]\+ [0-9]\+\)$$' integration.coverage.out > integration.coverage-bodged.out
|
||||
GO111MODULE=on $(GO) run build/gocovmerge.go integration.coverage-bodged.out coverage-bodged.out > coverage.all || (echo "gocovmerge failed"; echo "integration.coverage.out"; cat integration.coverage.out; echo "coverage.out"; cat coverage.out; exit 1)
|
||||
GO111MODULE=on $(GO) run -mod=vendor build/gocovmerge.go integration.coverage.out $(shell find . -type f -name "coverage.out") > coverage.all
|
||||
|
||||
.PHONY: unit-test-coverage
|
||||
unit-test-coverage:
|
||||
@echo "Running unit-test-coverage $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
|
||||
@$(GO) test $(GOTESTFLAGS) -timeout=20m -tags='$(TEST_TAGS)' -cover -coverprofile coverage.out $(GO_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -tags='sqlite sqlite_unlock_notify' -cover -coverprofile coverage.out $(GO_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
|
||||
|
||||
.PHONY: vendor
|
||||
vendor:
|
||||
$(GO) mod tidy && $(GO) mod vendor
|
||||
|
||||
.PHONY: gomod-check
|
||||
gomod-check:
|
||||
@$(GO) mod tidy
|
||||
@diff=$$(git diff go.sum); \
|
||||
.PHONY: test-vendor
|
||||
test-vendor: vendor
|
||||
@diff=$$(git diff vendor/); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run '$(GO) mod tidy' and commit the result:"; \
|
||||
echo "Please run 'make vendor' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
fi;
|
||||
|
||||
generate-ini-sqlite:
|
||||
sed -e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
|
||||
@@ -385,16 +381,15 @@ generate-ini-sqlite:
|
||||
|
||||
.PHONY: test-sqlite
|
||||
test-sqlite: integrations.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test
|
||||
|
||||
.PHONY: test-sqlite\#%
|
||||
test-sqlite\#%: integrations.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.run $(subst .,/,$*)
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-sqlite-migration
|
||||
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
|
||||
test-sqlite-migration: migrations.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./migrations.sqlite.test
|
||||
|
||||
generate-ini-mysql:
|
||||
sed -e 's|{{TEST_MYSQL_HOST}}|${TEST_MYSQL_HOST}|g' \
|
||||
@@ -406,16 +401,15 @@ generate-ini-mysql:
|
||||
|
||||
.PHONY: test-mysql
|
||||
test-mysql: integrations.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test
|
||||
|
||||
.PHONY: test-mysql\#%
|
||||
test-mysql\#%: integrations.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test -test.run $(subst .,/,$*)
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-mysql-migration
|
||||
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
|
||||
test-mysql-migration: migrations.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./migrations.mysql.test
|
||||
|
||||
generate-ini-mysql8:
|
||||
sed -e 's|{{TEST_MYSQL8_HOST}}|${TEST_MYSQL8_HOST}|g' \
|
||||
@@ -427,16 +421,15 @@ generate-ini-mysql8:
|
||||
|
||||
.PHONY: test-mysql8
|
||||
test-mysql8: integrations.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./integrations.mysql8.test
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql8.ini ./integrations.mysql8.test
|
||||
|
||||
.PHONY: test-mysql8\#%
|
||||
test-mysql8\#%: integrations.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./integrations.mysql8.test -test.run $(subst .,/,$*)
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql8.ini ./integrations.mysql8.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-mysql8-migration
|
||||
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
|
||||
test-mysql8-migration: migrations.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql8.ini ./migrations.mysql8.test
|
||||
|
||||
generate-ini-pgsql:
|
||||
sed -e 's|{{TEST_PGSQL_HOST}}|${TEST_PGSQL_HOST}|g' \
|
||||
@@ -449,16 +442,15 @@ generate-ini-pgsql:
|
||||
|
||||
.PHONY: test-pgsql
|
||||
test-pgsql: integrations.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test
|
||||
|
||||
.PHONY: test-pgsql\#%
|
||||
test-pgsql\#%: integrations.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test -test.run $(subst .,/,$*)
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-pgsql-migration
|
||||
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
|
||||
test-pgsql-migration: migrations.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./migrations.pgsql.test
|
||||
|
||||
generate-ini-mssql:
|
||||
sed -e 's|{{TEST_MSSQL_HOST}}|${TEST_MSSQL_HOST}|g' \
|
||||
@@ -470,54 +462,53 @@ generate-ini-mssql:
|
||||
|
||||
.PHONY: test-mssql
|
||||
test-mssql: integrations.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test
|
||||
|
||||
.PHONY: test-mssql\#%
|
||||
test-mssql\#%: integrations.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test -test.run $(subst .,/,$*)
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-mssql-migration
|
||||
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
|
||||
test-mssql-migration: migrations.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mssql.ini ./migrations.mssql.test -test.failfast
|
||||
|
||||
.PHONY: bench-sqlite
|
||||
bench-sqlite: integrations.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
.PHONY: bench-mysql
|
||||
bench-mysql: integrations.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
.PHONY: bench-mssql
|
||||
bench-mssql: integrations.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
.PHONY: bench-pgsql
|
||||
bench-pgsql: integrations.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test -test.cpuprofile=cpu.out -test.run DontRunTests -test.bench .
|
||||
|
||||
.PHONY: integration-test-coverage
|
||||
integration-test-coverage: integrations.cover.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.cover.test -test.coverprofile=integration.coverage.out
|
||||
GITEA_ROOT=${CURDIR} GITEA_CONF=integrations/mysql.ini ./integrations.cover.test -test.coverprofile=integration.coverage.out
|
||||
|
||||
integrations.mysql.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -o integrations.mysql.test
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.mysql.test
|
||||
|
||||
integrations.mysql8.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -o integrations.mysql8.test
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.mysql8.test
|
||||
|
||||
integrations.pgsql.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -o integrations.pgsql.test
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.pgsql.test
|
||||
|
||||
integrations.mssql.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -o integrations.mssql.test
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.mssql.test
|
||||
|
||||
integrations.sqlite.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -o integrations.sqlite.test -tags '$(TEST_TAGS)'
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -o integrations.sqlite.test -tags 'sqlite sqlite_unlock_notify'
|
||||
|
||||
integrations.cover.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -coverpkg $(shell echo $(GO_PACKAGES) | tr ' ' ',') -o integrations.cover.test
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -coverpkg $(shell echo $(GO_PACKAGES) | tr ' ' ',') -o integrations.cover.test
|
||||
|
||||
.PHONY: migrations.mysql.test
|
||||
migrations.mysql.test: $(GO_SOURCES)
|
||||
@@ -537,27 +528,7 @@ migrations.mssql.test: $(GO_SOURCES)
|
||||
|
||||
.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)'
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.sqlite.test -tags 'sqlite sqlite_unlock_notify'
|
||||
|
||||
.PHONY: check
|
||||
check: test
|
||||
@@ -570,21 +541,20 @@ install: $(wildcard *.go)
|
||||
build: frontend backend
|
||||
|
||||
.PHONY: frontend
|
||||
frontend: $(WEBPACK_DEST)
|
||||
frontend: node-check $(FOMANTIC_DEST) $(WEBPACK_DEST)
|
||||
|
||||
.PHONY: backend
|
||||
backend: go-check generate $(EXECUTABLE)
|
||||
|
||||
.PHONY: generate
|
||||
generate: $(TAGS_PREREQ)
|
||||
@echo "Running go generate..."
|
||||
@CC= GOOS= GOARCH= $(GO) generate -tags '$(TAGS)' $(GO_PACKAGES)
|
||||
CC= GOOS= GOARCH= $(GO) generate -mod=vendor -tags '$(TAGS)' $(GO_PACKAGES)
|
||||
|
||||
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build -mod=vendor $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
|
||||
|
||||
.PHONY: release
|
||||
release: frontend generate release-windows release-linux release-darwin release-copy release-compress vendor release-sources release-docs release-check
|
||||
release: frontend generate release-windows release-linux release-darwin release-copy release-compress release-sources release-docs release-check
|
||||
|
||||
$(DIST_DIRS):
|
||||
mkdir -p $(DIST_DIRS)
|
||||
@@ -592,12 +562,10 @@ $(DIST_DIRS):
|
||||
.PHONY: release-windows
|
||||
release-windows: | $(DIST_DIRS)
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
||||
GO111MODULE=off $(GO) get -u src.techknowlogick.com/xgo; \
|
||||
fi
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||
ifeq (,$(findstring gogit,$(TAGS)))
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" 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
|
||||
@echo "Warning: windows version is built using golang 1.14"
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" GO111MODULE=off xgo -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
@@ -605,9 +573,9 @@ endif
|
||||
.PHONY: release-linux
|
||||
release-linux: | $(DIST_DIRS)
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
||||
GO111MODULE=off $(GO) get -u src.techknowlogick.com/xgo; \
|
||||
fi
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" GO111MODULE=off xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/mips64le,linux/mips,linux/mipsle' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
@@ -615,9 +583,9 @@ endif
|
||||
.PHONY: release-darwin
|
||||
release-darwin: | $(DIST_DIRS)
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
||||
GO111MODULE=off $(GO) get -u src.techknowlogick.com/xgo; \
|
||||
fi
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" GO111MODULE=off xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin/amd64' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
@@ -633,16 +601,14 @@ release-check: | $(DIST_DIRS)
|
||||
.PHONY: release-compress
|
||||
release-compress: | $(DIST_DIRS)
|
||||
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install github.com/ulikunitz/xz/cmd/gxz@v0.5.10; \
|
||||
GO111MODULE=off $(GO) get -u github.com/ulikunitz/xz/cmd/gxz; \
|
||||
fi
|
||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
|
||||
|
||||
.PHONY: release-sources
|
||||
release-sources: | $(DIST_DIRS)
|
||||
release-sources: | $(DIST_DIRS) node_modules
|
||||
echo $(VERSION) > $(STORED_VERSION_FILE)
|
||||
# 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 .
|
||||
tar --exclude=./$(DIST) --exclude=./.git --exclude=./$(MAKE_EVIDENCE_DIR) --exclude=./node_modules/.cache --exclude=./$(AIR_TMP_DIR) -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz .
|
||||
rm -f $(STORED_VERSION_FILE)
|
||||
|
||||
.PHONY: release-docs
|
||||
@@ -668,22 +634,21 @@ npm-update: node-check | node_modules
|
||||
@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.*
|
||||
fomantic: $(FOMANTIC_DEST)
|
||||
|
||||
$(FOMANTIC_DEST): $(FOMANTIC_CONFIGS) | node_modules
|
||||
rm -rf $(FOMANTIC_DEST_DIR)
|
||||
cp -f web_src/fomantic/theme.config.less node_modules/fomantic-ui/src/theme.config
|
||||
cp -fr web_src/fomantic/_site/* node_modules/fomantic-ui/src/_site/
|
||||
npx gulp -f node_modules/fomantic-ui/gulpfile.js build
|
||||
@touch $(FOMANTIC_DEST)
|
||||
|
||||
.PHONY: webpack
|
||||
webpack: $(WEBPACK_DEST)
|
||||
|
||||
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
|
||||
@$(MAKE) -s node-check node_modules
|
||||
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json | node_modules
|
||||
rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||
npx webpack
|
||||
npx webpack --hide-modules --display-entrypoints=false
|
||||
@touch $(WEBPACK_DEST)
|
||||
|
||||
.PHONY: svg
|
||||
@@ -699,18 +664,7 @@ svg-check: svg
|
||||
echo "Please run 'make svg' and 'git add $(SVG_DEST_DIR)' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: lockfile-check
|
||||
lockfile-check:
|
||||
npm install --package-lock-only
|
||||
@diff=$$(git diff package-lock.json); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "package-lock.json is inconsistent with package.json"; \
|
||||
echo "Please run 'npm install --package-lock-only' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
fi;
|
||||
|
||||
.PHONY: update-translations
|
||||
update-translations:
|
||||
@@ -730,40 +684,23 @@ generate-license:
|
||||
generate-gitignore:
|
||||
GO111MODULE=on $(GO) run build/generate-gitignores.go
|
||||
|
||||
.PHONY: generate-images
|
||||
generate-images: | node_modules
|
||||
npm install --no-save --no-package-lock fabric@4 imagemin-zopfli@7
|
||||
node build/generate-images.js $(TAGS)
|
||||
|
||||
.PHONY: generate-manpage
|
||||
generate-manpage:
|
||||
@[ -f gitea ] || make backend
|
||||
@mkdir -p man/man1/ man/man5
|
||||
@./gitea docs --man > man/man1/gitea.1
|
||||
@gzip -9 man/man1/gitea.1 && echo man/man1/gitea.1.gz created
|
||||
@#TODO A smal script witch format config-cheat-sheet.en-us.md nicely to suit as config man page
|
||||
.PHONY: generate-images
|
||||
generate-images:
|
||||
npm install --no-save --no-package-lock xmldom fabric imagemin-zopfli
|
||||
node build/generate-images.js
|
||||
|
||||
.PHONY: pr\#%
|
||||
pr\#%: clean-all
|
||||
$(GO) run contrib/pr/checkout.go $*
|
||||
|
||||
.PHONY: golangci-lint
|
||||
golangci-lint: golangci-lint-check
|
||||
golangci-lint run --timeout 10m
|
||||
|
||||
.PHONY: golangci-lint-check
|
||||
golangci-lint-check:
|
||||
$(eval GOLANGCI_LINT_VERSION := $(shell printf "%03d%03d%03d" $(shell golangci-lint --version | grep -Eo '[0-9]+\.[0-9.]+' | tr '.' ' ');))
|
||||
$(eval MIN_GOLANGCI_LINT_VER_FMT := $(shell printf "%g.%g.%g" $(shell echo $(MIN_GOLANGCI_LINT_VERSION) | grep -o ...)))
|
||||
golangci-lint:
|
||||
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
echo "Downloading golangci-lint v${MIN_GOLANGCI_LINT_VER_FMT}"; \
|
||||
export BINARY="golangci-lint"; \
|
||||
curl -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/v${MIN_GOLANGCI_LINT_VER_FMT}/install.sh" | sh -s -- -b $(GOPATH)/bin v$(MIN_GOLANGCI_LINT_VER_FMT); \
|
||||
elif [ "$(GOLANGCI_LINT_VERSION)" -lt "$(MIN_GOLANGCI_LINT_VERSION)" ]; then \
|
||||
echo "Downloading newer version of golangci-lint v${MIN_GOLANGCI_LINT_VER_FMT}"; \
|
||||
export BINARY="golangci-lint"; \
|
||||
curl -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/v${MIN_GOLANGCI_LINT_VER_FMT}/install.sh" | sh -s -- -b $(GOPATH)/bin v$(MIN_GOLANGCI_LINT_VER_FMT); \
|
||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.31.0; \
|
||||
fi
|
||||
golangci-lint run --timeout 5m
|
||||
|
||||
.PHONY: docker
|
||||
docker:
|
||||
@@ -772,7 +709,7 @@ docker:
|
||||
|
||||
.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
|
||||
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
|
||||
|
96
README.md
96
README.md
@@ -1,52 +1,19 @@
|
||||
<p align="center">
|
||||
<a href="https://gitea.io/">
|
||||
<img alt="Gitea" src="https://raw.githubusercontent.com/go-gitea/gitea/main/public/img/gitea.svg" width="220"/>
|
||||
</a>
|
||||
</p>
|
||||
<h1 align="center">Gitea - Git with a cup of tea</h1>
|
||||
[简体中文](README_ZH.md)
|
||||
|
||||
<p align="center">
|
||||
<a href="https://drone.gitea.io/go-gitea/gitea" title="Build Status">
|
||||
<img src="https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg?ref=refs/heads/main">
|
||||
</a>
|
||||
<a href="https://discord.gg/Gitea" title="Join the Discord chat at https://discord.gg/Gitea">
|
||||
<img src="https://img.shields.io/discord/322538954119184384.svg">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/go-gitea/gitea" title="Codecov">
|
||||
<img src="https://codecov.io/gh/go-gitea/gitea/branch/main/graph/badge.svg">
|
||||
</a>
|
||||
<a href="https://goreportcard.com/report/code.gitea.io/gitea" title="Go Report Card">
|
||||
<img src="https://goreportcard.com/badge/code.gitea.io/gitea">
|
||||
</a>
|
||||
<a href="https://godoc.org/code.gitea.io/gitea" title="GoDoc">
|
||||
<img src="https://godoc.org/code.gitea.io/gitea?status.svg">
|
||||
</a>
|
||||
<a href="https://github.com/go-gitea/gitea/releases/latest" title="GitHub release">
|
||||
<img src="https://img.shields.io/github/release/go-gitea/gitea.svg">
|
||||
</a>
|
||||
<a href="https://www.codetriage.com/go-gitea/gitea" title="Help Contribute to Open Source">
|
||||
<img src="https://www.codetriage.com/go-gitea/gitea/badges/users.svg">
|
||||
</a>
|
||||
<a href="https://opencollective.com/gitea" title="Become a backer/sponsor of gitea">
|
||||
<img src="https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen">
|
||||
</a>
|
||||
<a href="https://opensource.org/licenses/MIT" title="License: MIT">
|
||||
<img src="https://img.shields.io/badge/License-MIT-blue.svg">
|
||||
</a>
|
||||
<a href="https://crowdin.com/project/gitea" title="Crowdin">
|
||||
<img src="https://badges.crowdin.net/gitea/localized.svg">
|
||||
</a>
|
||||
<a href="https://www.tickgit.com/browse?repo=github.com/go-gitea/gitea&branch=main" title="TODOs">
|
||||
<img src="https://badgen.net/https/api.tickgit.com/badgen/github.com/go-gitea/gitea/main">
|
||||
</a>
|
||||
<a href="https://www.bountysource.com/teams/gitea" title="Bountysource">
|
||||
<img src="https://img.shields.io/bountysource/team/gitea/activity">
|
||||
</a>
|
||||
</p>
|
||||
<h1> <img src="https://raw.githubusercontent.com/go-gitea/gitea/master/public/img/gitea-192.png" alt="logo" width="30" height="30"> Gitea - Git with a cup of tea</h1>
|
||||
|
||||
<p align="center">
|
||||
<a href="README_ZH.md">View the chinese version of this document</a>
|
||||
</p>
|
||||
[](https://drone.gitea.io/go-gitea/gitea)
|
||||
[](https://discord.gg/Gitea)
|
||||
[](https://microbadger.com/images/gitea/gitea "Get your own image badge on microbadger.com")
|
||||
[](https://codecov.io/gh/go-gitea/gitea)
|
||||
[](https://goreportcard.com/report/code.gitea.io/gitea)
|
||||
[](https://godoc.org/code.gitea.io/gitea)
|
||||
[](https://github.com/go-gitea/gitea/releases/latest)
|
||||
[](https://www.codetriage.com/go-gitea/gitea)
|
||||
[](https://opencollective.com/gitea)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://crowdin.com/project/gitea)
|
||||
[](https://www.tickgit.com/browse?repo=github.com/go-gitea/gitea)
|
||||
|
||||
## Purpose
|
||||
|
||||
@@ -67,18 +34,20 @@ From the root of the source tree, run:
|
||||
|
||||
TAGS="bindata" make build
|
||||
|
||||
or if SQLite support is required:
|
||||
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 LTS](https://nodejs.org/en/download/) or greater and Internet connectivity to download npm dependencies.
|
||||
- `make backend` which requires [Go 1.13](https://golang.org/dl/) or greater.
|
||||
- `make frontend` which requires [Node.js 10.13](https://nodejs.org/en/download/) or greater.
|
||||
|
||||
When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js and Internet connectivity.
|
||||
If pre-built frontend files are present it is possible to only build the backend:
|
||||
|
||||
Parallelism (`make -j <num>`) is not supported.
|
||||
TAGS="bindata" make backend
|
||||
|
||||
Parallelism is not supported for these targets, so please don't include `-j <num>`.
|
||||
|
||||
More info: https://docs.gitea.io/en-us/install-from-source/
|
||||
|
||||
@@ -98,24 +67,13 @@ NOTES:
|
||||
1. **YOU MUST READ THE [CONTRIBUTORS GUIDE](CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST.**
|
||||
2. If you have found a vulnerability in the project, please write privately to **security@gitea.io**. Thanks!
|
||||
|
||||
## Translating
|
||||
|
||||
Translations are done through Crowdin. If you want to translate to a new language ask one of the managers in the Crowdin project to add a new language there.
|
||||
|
||||
You can also just create an issue for adding a language or ask on discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty but we hope fo fill it as questions pop up.
|
||||
|
||||
https://docs.gitea.io/en-us/translation-guidelines/
|
||||
|
||||
[](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).
|
||||
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 [forum](https://discourse.gitea.io/)!
|
||||
|
||||
## Authors
|
||||
|
||||
@@ -157,7 +115,7 @@ We're [working on it](https://github.com/go-gitea/gitea/issues/1029).
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License.
|
||||
See the [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) file
|
||||
See the [LICENSE](https://github.com/go-gitea/gitea/blob/master/LICENSE) file
|
||||
for the full license text.
|
||||
|
||||
## Screenshots
|
||||
|
67
README_ZH.md
67
README_ZH.md
@@ -1,52 +1,18 @@
|
||||
<p align="center">
|
||||
<a href="https://gitea.io/">
|
||||
<img alt="Gitea" src="https://raw.githubusercontent.com/go-gitea/gitea/main/public/img/gitea.svg" width="220"/>
|
||||
</a>
|
||||
</p>
|
||||
<h1 align="center">Gitea - Git with a cup of tea</h1>
|
||||
[English](README.md)
|
||||
|
||||
<p align="center">
|
||||
<a href="https://drone.gitea.io/go-gitea/gitea" title="Build Status">
|
||||
<img src="https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg?ref=refs/heads/main">
|
||||
</a>
|
||||
<a href="https://discord.gg/Gitea" title="Join the Discord chat at https://discord.gg/Gitea">
|
||||
<img src="https://img.shields.io/discord/322538954119184384.svg">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/go-gitea/gitea" title="Codecov">
|
||||
<img src="https://codecov.io/gh/go-gitea/gitea/branch/main/graph/badge.svg">
|
||||
</a>
|
||||
<a href="https://goreportcard.com/report/code.gitea.io/gitea" title="Go Report Card">
|
||||
<img src="https://goreportcard.com/badge/code.gitea.io/gitea">
|
||||
</a>
|
||||
<a href="https://godoc.org/code.gitea.io/gitea" title="GoDoc">
|
||||
<img src="https://godoc.org/code.gitea.io/gitea?status.svg">
|
||||
</a>
|
||||
<a href="https://github.com/go-gitea/gitea/releases/latest" title="GitHub release">
|
||||
<img src="https://img.shields.io/github/release/go-gitea/gitea.svg">
|
||||
</a>
|
||||
<a href="https://www.codetriage.com/go-gitea/gitea" title="Help Contribute to Open Source">
|
||||
<img src="https://www.codetriage.com/go-gitea/gitea/badges/users.svg">
|
||||
</a>
|
||||
<a href="https://opencollective.com/gitea" title="Become a backer/sponsor of gitea">
|
||||
<img src="https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen">
|
||||
</a>
|
||||
<a href="https://opensource.org/licenses/MIT" title="License: MIT">
|
||||
<img src="https://img.shields.io/badge/License-MIT-blue.svg">
|
||||
</a>
|
||||
<a href="https://crowdin.com/project/gitea" title="Crowdin">
|
||||
<img src="https://badges.crowdin.net/gitea/localized.svg">
|
||||
</a>
|
||||
<a href="https://www.tickgit.com/browse?repo=github.com/go-gitea/gitea&branch=main" title="TODOs">
|
||||
<img src="https://badgen.net/https/api.tickgit.com/badgen/github.com/go-gitea/gitea/main">
|
||||
</a>
|
||||
<a href="https://img.shields.io/bountysource/team/gitea" title="Bountysource">
|
||||
<img src="https://img.shields.io/bountysource/team/gitea/activity">
|
||||
</a>
|
||||
</p>
|
||||
<h1> <img src="https://raw.githubusercontent.com/go-gitea/gitea/master/public/img/gitea-192.png" alt="logo" width="30" height="30"> Gitea - Git with a cup of tea</h1>
|
||||
|
||||
<p align="center">
|
||||
<a href="README.md">View the english version of this document</a>
|
||||
</p>
|
||||
[](https://drone.gitea.io/go-gitea/gitea)
|
||||
[](https://discord.gg/Gitea)
|
||||
[](https://microbadger.com/images/gitea/gitea "Get your own image badge on microbadger.com")
|
||||
[](https://codecov.io/gh/go-gitea/gitea)
|
||||
[](https://goreportcard.com/report/code.gitea.io/gitea)
|
||||
[](https://godoc.org/code.gitea.io/gitea)
|
||||
[](https://github.com/go-gitea/gitea/releases/latest)
|
||||
[](https://www.codetriage.com/go-gitea/gitea)
|
||||
[](https://opencollective.com/gitea)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://crowdin.com/project/gitea)
|
||||
|
||||
## 目标
|
||||
|
||||
@@ -68,11 +34,6 @@ Gitea 的首要目标是创建一个极易安装,运行非常快速,安装
|
||||
|
||||
Fork -> Patch -> Push -> Pull Request
|
||||
|
||||
## 翻译
|
||||
|
||||
多语言翻译是基于Crowdin进行的.
|
||||
[](https://crowdin.com/project/gitea)
|
||||
|
||||
## 作者
|
||||
|
||||
* [Maintainers](https://github.com/orgs/go-gitea/people)
|
||||
@@ -81,7 +42,7 @@ Fork -> Patch -> Push -> Pull Request
|
||||
|
||||
## 授权许可
|
||||
|
||||
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件中。
|
||||
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/go-gitea/gitea/blob/master/LICENSE) 文件中。
|
||||
|
||||
## 截图
|
||||
|
||||
|
192
assets/logo.svg
192
assets/logo.svg
@@ -1,31 +1,161 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" id="main_outline" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 640 640" style="enable-background:new 0 0 640 640;" xml:space="preserve">
|
||||
<g>
|
||||
<path id="teabag" style="fill:#FFFFFF" d="M395.9,484.2l-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5,21.2-17.9,33.8-11.8
|
||||
c17.2,8.3,27.1,13,27.1,13l-0.1-109.2l16.7-0.1l0.1,117.1c0,0,57.4,24.2,83.1,40.1c3.7,2.3,10.2,6.8,12.9,14.4
|
||||
c2.1,6.1,2,13.1-1,19.3l-61,126.9C423.6,484.9,408.4,490.3,395.9,484.2z"/>
|
||||
<g>
|
||||
<g>
|
||||
<path style="fill:#609926" d="M622.7,149.8c-4.1-4.1-9.6-4-9.6-4s-117.2,6.6-177.9,8c-13.3,0.3-26.5,0.6-39.6,0.7c0,39.1,0,78.2,0,117.2
|
||||
c-5.5-2.6-11.1-5.3-16.6-7.9c0-36.4-0.1-109.2-0.1-109.2c-29,0.4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5
|
||||
c-9.8-0.6-22.5-2.1-39,1.5c-8.7,1.8-33.5,7.4-53.8,26.9C-4.9,212.4,6.6,276.2,8,285.8c1.7,11.7,6.9,44.2,31.7,72.5
|
||||
c45.8,56.1,144.4,54.8,144.4,54.8s12.1,28.9,30.6,55.5c25,33.1,50.7,58.9,75.7,62c63,0,188.9-0.1,188.9-0.1s12,0.1,28.3-10.3
|
||||
c14-8.5,26.5-23.4,26.5-23.4s12.9-13.8,30.9-45.3c5.5-9.7,10.1-19.1,14.1-28c0,0,55.2-117.1,55.2-231.1
|
||||
C633.2,157.9,624.7,151.8,622.7,149.8z M125.6,353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6,321.8,60,295.4
|
||||
c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5,38.5-30c13.8-3.7,31-3.1,31-3.1s7.1,59.4,15.7,94.2c7.2,29.2,24.8,77.7,24.8,77.7
|
||||
S142.5,359.9,125.6,353.9z M425.9,461.5c0,0-6.1,14.5-19.6,15.4c-5.8,0.4-10.3-1.2-10.3-1.2s-0.3-0.1-5.3-2.1l-112.9-55
|
||||
c0,0-10.9-5.7-12.8-15.6c-2.2-8.1,2.7-18.1,2.7-18.1L322,273c0,0,4.8-9.7,12.2-13c0.6-0.3,2.3-1,4.5-1.5c8.1-2.1,18,2.8,18,2.8
|
||||
l110.7,53.7c0,0,12.6,5.7,15.3,16.2c1.9,7.4-0.5,14-1.8,17.2C474.6,363.8,425.9,461.5,425.9,461.5z"/>
|
||||
<path style="fill:#609926" d="M326.8,380.1c-8.2,0.1-15.4,5.8-17.3,13.8c-1.9,8,2,16.3,9.1,20c7.7,4,17.5,1.8,22.7-5.4
|
||||
c5.1-7.1,4.3-16.9-1.8-23.1l24-49.1c1.5,0.1,3.7,0.2,6.2-0.5c4.1-0.9,7.1-3.6,7.1-3.6c4.2,1.8,8.6,3.8,13.2,6.1
|
||||
c4.8,2.4,9.3,4.9,13.4,7.3c0.9,0.5,1.8,1.1,2.8,1.9c1.6,1.3,3.4,3.1,4.7,5.5c1.9,5.5-1.9,14.9-1.9,14.9
|
||||
c-2.3,7.6-18.4,40.6-18.4,40.6c-8.1-0.2-15.3,5-17.7,12.5c-2.6,8.1,1.1,17.3,8.9,21.3c7.8,4,17.4,1.7,22.5-5.3
|
||||
c5-6.8,4.6-16.3-1.1-22.6c1.9-3.7,3.7-7.4,5.6-11.3c5-10.4,13.5-30.4,13.5-30.4c0.9-1.7,5.7-10.3,2.7-21.3
|
||||
c-2.5-11.4-12.6-16.7-12.6-16.7c-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3c4.7-9.7,9.4-19.3,14.1-29
|
||||
c-4.1-2-8.1-4-12.2-6.1c-4.8,9.8-9.7,19.7-14.5,29.5c-6.7-0.1-12.9,3.5-16.1,9.4c-3.4,6.3-2.7,14.1,1.9,19.8
|
||||
C343.2,346.5,335,363.3,326.8,380.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 135.46667 135.46667"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
sodipodi:docname="logo.svg"
|
||||
inkscape:version="0.92.1 r15371"
|
||||
inkscape:export-filename=""
|
||||
inkscape:export-xdpi="48.000004"
|
||||
inkscape:export-ydpi="48.000004">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
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"
|
||||
class="detail-remove"
|
||||
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>
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 5.6 KiB |
11
build.go
11
build.go
@@ -2,8 +2,7 @@
|
||||
// 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
|
||||
//+build vendor
|
||||
|
||||
package main
|
||||
|
||||
@@ -11,6 +10,14 @@ package main
|
||||
// These libraries will not be included in a normal compilation.
|
||||
|
||||
import (
|
||||
// for lint
|
||||
_ "github.com/mgechev/dots"
|
||||
_ "github.com/mgechev/revive/formatter"
|
||||
_ "github.com/mgechev/revive/lint"
|
||||
_ "github.com/mgechev/revive/rule"
|
||||
_ "github.com/mitchellh/go-homedir"
|
||||
_ "github.com/pelletier/go-toml"
|
||||
|
||||
// for embed
|
||||
_ "github.com/shurcooL/vfsgen"
|
||||
|
||||
|
@@ -1,284 +0,0 @@
|
||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/build/codeformat"
|
||||
)
|
||||
|
||||
// Windows has a limitation for command line arguments, the size can not exceed 32KB.
|
||||
// So we have to feed the files to some tools (like gofmt/misspell) batch by batch
|
||||
|
||||
// We also introduce a `gitea-fmt` command, it does better import formatting than gofmt/goimports. `gitea-fmt` calls `gofmt` internally.
|
||||
|
||||
var optionLogVerbose bool
|
||||
|
||||
func logVerbose(msg string, args ...interface{}) {
|
||||
if optionLogVerbose {
|
||||
log.Printf(msg, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func passThroughCmd(cmd string, args []string) error {
|
||||
foundCmd, err := exec.LookPath(cmd)
|
||||
if err != nil {
|
||||
log.Fatalf("can not find cmd: %s", cmd)
|
||||
}
|
||||
c := exec.Cmd{
|
||||
Path: foundCmd,
|
||||
Args: args,
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
}
|
||||
return c.Run()
|
||||
}
|
||||
|
||||
type fileCollector struct {
|
||||
dirs []string
|
||||
includePatterns []*regexp.Regexp
|
||||
excludePatterns []*regexp.Regexp
|
||||
batchSize int
|
||||
}
|
||||
|
||||
func newFileCollector(fileFilter string, batchSize int) (*fileCollector, error) {
|
||||
co := &fileCollector{batchSize: batchSize}
|
||||
if fileFilter == "go-own" {
|
||||
co.dirs = []string{
|
||||
"build",
|
||||
"cmd",
|
||||
"contrib",
|
||||
"integrations",
|
||||
"models",
|
||||
"modules",
|
||||
"routers",
|
||||
"services",
|
||||
"tools",
|
||||
}
|
||||
co.includePatterns = append(co.includePatterns, regexp.MustCompile(`.*\.go$`))
|
||||
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`.*\bbindata\.go$`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`integrations/gitea-repositories-meta`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`integrations/migration-test`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`modules/git/tests`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`models/fixtures`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`models/migrations/fixtures`))
|
||||
co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`services/gitdiff/testdata`))
|
||||
}
|
||||
|
||||
if co.dirs == nil {
|
||||
return nil, fmt.Errorf("unknown file-filter: %s", fileFilter)
|
||||
}
|
||||
return co, nil
|
||||
}
|
||||
|
||||
func (fc *fileCollector) matchPatterns(path string, regexps []*regexp.Regexp) bool {
|
||||
path = strings.ReplaceAll(path, "\\", "/")
|
||||
for _, re := range regexps {
|
||||
if re.MatchString(path) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (fc *fileCollector) collectFiles() (res [][]string, err error) {
|
||||
var batch []string
|
||||
for _, dir := range fc.dirs {
|
||||
err = filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
|
||||
include := len(fc.includePatterns) == 0 || fc.matchPatterns(path, fc.includePatterns)
|
||||
exclude := fc.matchPatterns(path, fc.excludePatterns)
|
||||
process := include && !exclude
|
||||
if !process {
|
||||
if d.IsDir() {
|
||||
if exclude {
|
||||
logVerbose("exclude dir %s", path)
|
||||
return filepath.SkipDir
|
||||
}
|
||||
// for a directory, if it is not excluded explicitly, we should walk into
|
||||
return nil
|
||||
}
|
||||
// for a file, we skip it if it shouldn't be processed
|
||||
logVerbose("skip process %s", path)
|
||||
return nil
|
||||
}
|
||||
if d.IsDir() {
|
||||
// skip dir, we don't add dirs to the file list now
|
||||
return nil
|
||||
}
|
||||
if len(batch) >= fc.batchSize {
|
||||
res = append(res, batch)
|
||||
batch = nil
|
||||
}
|
||||
batch = append(batch, path)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
res = append(res, batch)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// substArgFiles expands the {file-list} to a real file list for commands
|
||||
func substArgFiles(args []string, files []string) []string {
|
||||
for i, s := range args {
|
||||
if s == "{file-list}" {
|
||||
newArgs := append(args[:i], files...)
|
||||
newArgs = append(newArgs, args[i+1:]...)
|
||||
return newArgs
|
||||
}
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func exitWithCmdErrors(subCmd string, subArgs []string, cmdErrors []error) {
|
||||
for _, err := range cmdErrors {
|
||||
if err != nil {
|
||||
if exitError, ok := err.(*exec.ExitError); ok {
|
||||
exitCode := exitError.ExitCode()
|
||||
log.Printf("run command failed (code=%d): %s %v", exitCode, subCmd, subArgs)
|
||||
os.Exit(exitCode)
|
||||
} else {
|
||||
log.Fatalf("run command failed (err=%s) %s %v", err, subCmd, subArgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseArgs() (mainOptions map[string]string, subCmd string, subArgs []string) {
|
||||
mainOptions = map[string]string{}
|
||||
for i := 1; i < len(os.Args); i++ {
|
||||
arg := os.Args[i]
|
||||
if arg == "" {
|
||||
break
|
||||
}
|
||||
if arg[0] == '-' {
|
||||
arg = strings.TrimPrefix(arg, "-")
|
||||
arg = strings.TrimPrefix(arg, "-")
|
||||
fields := strings.SplitN(arg, "=", 2)
|
||||
if len(fields) == 1 {
|
||||
mainOptions[fields[0]] = "1"
|
||||
} else {
|
||||
mainOptions[fields[0]] = fields[1]
|
||||
}
|
||||
} else {
|
||||
subCmd = arg
|
||||
subArgs = os.Args[i+1:]
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func showUsage() {
|
||||
fmt.Printf(`Usage: %[1]s [options] {command} [arguments]
|
||||
|
||||
Options:
|
||||
--verbose
|
||||
--file-filter=go-own
|
||||
--batch-size=100
|
||||
|
||||
Commands:
|
||||
%[1]s gofmt ...
|
||||
%[1]s misspell ...
|
||||
|
||||
Arguments:
|
||||
{file-list} the file list
|
||||
|
||||
Example:
|
||||
%[1]s gofmt -s -d {file-list}
|
||||
|
||||
`, "file-batch-exec")
|
||||
}
|
||||
|
||||
func newFileCollectorFromMainOptions(mainOptions map[string]string) (fc *fileCollector, err error) {
|
||||
fileFilter := mainOptions["file-filter"]
|
||||
if fileFilter == "" {
|
||||
fileFilter = "go-own"
|
||||
}
|
||||
batchSize, _ := strconv.Atoi(mainOptions["batch-size"])
|
||||
if batchSize == 0 {
|
||||
batchSize = 100
|
||||
}
|
||||
|
||||
return newFileCollector(fileFilter, batchSize)
|
||||
}
|
||||
|
||||
func containsString(a []string, s string) bool {
|
||||
for _, v := range a {
|
||||
if v == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func giteaFormatGoImports(files []string) error {
|
||||
for _, file := range files {
|
||||
if err := codeformat.FormatGoImports(file); err != nil {
|
||||
log.Printf("failed to format go imports: %s, err=%v", file, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
mainOptions, subCmd, subArgs := parseArgs()
|
||||
if subCmd == "" {
|
||||
showUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
optionLogVerbose = mainOptions["verbose"] != ""
|
||||
|
||||
fc, err := newFileCollectorFromMainOptions(mainOptions)
|
||||
if err != nil {
|
||||
log.Fatalf("can not create file collector: %s", err.Error())
|
||||
}
|
||||
|
||||
fileBatches, err := fc.collectFiles()
|
||||
if err != nil {
|
||||
log.Fatalf("can not collect files: %s", err.Error())
|
||||
}
|
||||
|
||||
processed := 0
|
||||
var cmdErrors []error
|
||||
for _, files := range fileBatches {
|
||||
if len(files) == 0 {
|
||||
break
|
||||
}
|
||||
substArgs := substArgFiles(subArgs, files)
|
||||
logVerbose("batch cmd: %s %v", subCmd, substArgs)
|
||||
switch subCmd {
|
||||
case "gitea-fmt":
|
||||
if containsString(subArgs, "-w") {
|
||||
cmdErrors = append(cmdErrors, giteaFormatGoImports(files))
|
||||
}
|
||||
cmdErrors = append(cmdErrors, passThroughCmd("gofmt", substArgs))
|
||||
case "misspell":
|
||||
cmdErrors = append(cmdErrors, passThroughCmd("misspell", substArgs))
|
||||
default:
|
||||
log.Fatalf("unknown cmd: %s %v", subCmd, subArgs)
|
||||
}
|
||||
processed += len(files)
|
||||
}
|
||||
|
||||
logVerbose("processed %d files", processed)
|
||||
exitWithCmdErrors(subCmd, subArgs, cmdErrors)
|
||||
}
|
@@ -1,187 +0,0 @@
|
||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package codeformat
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var importPackageGroupOrders = map[string]int{
|
||||
"": 1, // internal
|
||||
"code.gitea.io/gitea/": 2,
|
||||
}
|
||||
|
||||
var errInvalidCommentBetweenImports = errors.New("comments between imported packages are invalid, please move comments to the end of the package line")
|
||||
|
||||
var importBlockBegin = []byte("\nimport (\n")
|
||||
var importBlockEnd = []byte("\n)")
|
||||
|
||||
type importLineParsed struct {
|
||||
group string
|
||||
pkg string
|
||||
content string
|
||||
}
|
||||
|
||||
func parseImportLine(line string) (*importLineParsed, error) {
|
||||
il := &importLineParsed{content: line}
|
||||
p1 := strings.IndexRune(line, '"')
|
||||
if p1 == -1 {
|
||||
return nil, errors.New("invalid import line: " + line)
|
||||
}
|
||||
p1++
|
||||
p := strings.IndexRune(line[p1:], '"')
|
||||
if p == -1 {
|
||||
return nil, errors.New("invalid import line: " + line)
|
||||
}
|
||||
p2 := p1 + p
|
||||
il.pkg = line[p1:p2]
|
||||
|
||||
pDot := strings.IndexRune(il.pkg, '.')
|
||||
pSlash := strings.IndexRune(il.pkg, '/')
|
||||
if pDot != -1 && pDot < pSlash {
|
||||
il.group = "domain-package"
|
||||
}
|
||||
for groupName := range importPackageGroupOrders {
|
||||
if groupName == "" {
|
||||
continue // skip internal
|
||||
}
|
||||
if strings.HasPrefix(il.pkg, groupName) {
|
||||
il.group = groupName
|
||||
}
|
||||
}
|
||||
return il, nil
|
||||
}
|
||||
|
||||
type importLineGroup []*importLineParsed
|
||||
type importLineGroupMap map[string]importLineGroup
|
||||
|
||||
func formatGoImports(contentBytes []byte) ([]byte, error) {
|
||||
p1 := bytes.Index(contentBytes, importBlockBegin)
|
||||
if p1 == -1 {
|
||||
return nil, nil
|
||||
}
|
||||
p1 += len(importBlockBegin)
|
||||
p := bytes.Index(contentBytes[p1:], importBlockEnd)
|
||||
if p == -1 {
|
||||
return nil, nil
|
||||
}
|
||||
p2 := p1 + p
|
||||
|
||||
importGroups := importLineGroupMap{}
|
||||
r := bytes.NewBuffer(contentBytes[p1:p2])
|
||||
eof := false
|
||||
for !eof {
|
||||
line, err := r.ReadString('\n')
|
||||
eof = err == io.EOF
|
||||
if err != nil && !eof {
|
||||
return nil, err
|
||||
}
|
||||
line = strings.TrimSpace(line)
|
||||
if line != "" {
|
||||
if strings.HasPrefix(line, "//") || strings.HasPrefix(line, "/*") {
|
||||
return nil, errInvalidCommentBetweenImports
|
||||
}
|
||||
importLine, err := parseImportLine(line)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
importGroups[importLine.group] = append(importGroups[importLine.group], importLine)
|
||||
}
|
||||
}
|
||||
|
||||
var groupNames []string
|
||||
for groupName, importLines := range importGroups {
|
||||
groupNames = append(groupNames, groupName)
|
||||
sort.Slice(importLines, func(i, j int) bool {
|
||||
return strings.Compare(importLines[i].pkg, importLines[j].pkg) < 0
|
||||
})
|
||||
}
|
||||
|
||||
sort.Slice(groupNames, func(i, j int) bool {
|
||||
n1 := groupNames[i]
|
||||
n2 := groupNames[j]
|
||||
o1 := importPackageGroupOrders[n1]
|
||||
o2 := importPackageGroupOrders[n2]
|
||||
if o1 != 0 && o2 != 0 {
|
||||
return o1 < o2
|
||||
}
|
||||
if o1 == 0 && o2 == 0 {
|
||||
return strings.Compare(n1, n2) < 0
|
||||
}
|
||||
return o1 != 0
|
||||
})
|
||||
|
||||
formattedBlock := bytes.Buffer{}
|
||||
for _, groupName := range groupNames {
|
||||
hasNormalImports := false
|
||||
hasDummyImports := false
|
||||
// non-dummy import comes first
|
||||
for _, importLine := range importGroups[groupName] {
|
||||
if strings.HasPrefix(importLine.content, "_") {
|
||||
hasDummyImports = true
|
||||
} else {
|
||||
formattedBlock.WriteString("\t" + importLine.content + "\n")
|
||||
hasNormalImports = true
|
||||
}
|
||||
}
|
||||
// dummy (_ "pkg") comes later
|
||||
if hasDummyImports {
|
||||
if hasNormalImports {
|
||||
formattedBlock.WriteString("\n")
|
||||
}
|
||||
for _, importLine := range importGroups[groupName] {
|
||||
if strings.HasPrefix(importLine.content, "_") {
|
||||
formattedBlock.WriteString("\t" + importLine.content + "\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
formattedBlock.WriteString("\n")
|
||||
}
|
||||
formattedBlockBytes := bytes.TrimRight(formattedBlock.Bytes(), "\n")
|
||||
|
||||
var formattedBytes []byte
|
||||
formattedBytes = append(formattedBytes, contentBytes[:p1]...)
|
||||
formattedBytes = append(formattedBytes, formattedBlockBytes...)
|
||||
formattedBytes = append(formattedBytes, contentBytes[p2:]...)
|
||||
return formattedBytes, nil
|
||||
}
|
||||
|
||||
//FormatGoImports format the imports by our rules (see unit tests)
|
||||
func FormatGoImports(file string) error {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var contentBytes []byte
|
||||
{
|
||||
defer f.Close()
|
||||
contentBytes, err = io.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
formattedBytes, err := formatGoImports(contentBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if formattedBytes == nil {
|
||||
return nil
|
||||
}
|
||||
if bytes.Equal(contentBytes, formattedBytes) {
|
||||
return nil
|
||||
}
|
||||
f, err = os.OpenFile(file, os.O_TRUNC|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = f.Write(formattedBytes)
|
||||
return err
|
||||
}
|
@@ -1,125 +0,0 @@
|
||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package codeformat
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFormatImportsSimple(t *testing.T) {
|
||||
formatted, err := formatGoImports([]byte(`
|
||||
package codeformat
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
`))
|
||||
|
||||
expected := `
|
||||
package codeformat
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
`
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, string(formatted))
|
||||
}
|
||||
|
||||
func TestFormatImportsGroup(t *testing.T) {
|
||||
// gofmt/goimports won't group the packages, for example, they produce such code:
|
||||
// "bytes"
|
||||
// "image"
|
||||
// (a blank line)
|
||||
// "fmt"
|
||||
// "image/color/palette"
|
||||
// our formatter does better, and these packages are grouped into one.
|
||||
|
||||
formatted, err := formatGoImports([]byte(`
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
_ "image/gif" // for processing gif images
|
||||
_ "image/jpeg" // for processing jpeg images
|
||||
_ "image/png" // for processing png images
|
||||
|
||||
"code.gitea.io/other/package"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/the/package"
|
||||
|
||||
"github.com/issue9/identicon"
|
||||
"github.com/nfnt/resize"
|
||||
"github.com/oliamb/cutter"
|
||||
)
|
||||
`))
|
||||
|
||||
expected := `
|
||||
package test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
_ "image/gif" // for processing gif images
|
||||
_ "image/jpeg" // for processing jpeg images
|
||||
_ "image/png" // for processing png images
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"code.gitea.io/other/package"
|
||||
"github.com/issue9/identicon"
|
||||
"github.com/nfnt/resize"
|
||||
"github.com/oliamb/cutter"
|
||||
"xorm.io/the/package"
|
||||
)
|
||||
`
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, string(formatted))
|
||||
}
|
||||
|
||||
func TestFormatImportsInvalidComment(t *testing.T) {
|
||||
// why we shouldn't write comments between imports: it breaks the grouping of imports
|
||||
// for example:
|
||||
// "pkg1"
|
||||
// "pkg2"
|
||||
// // a comment
|
||||
// "pkgA"
|
||||
// "pkgB"
|
||||
// the comment splits the packages into two groups, pkg1/2 are sorted separately, pkgA/B are sorted separately
|
||||
// we don't want such code, so the code should be:
|
||||
// "pkg1"
|
||||
// "pkg2"
|
||||
// "pkgA" // a comment
|
||||
// "pkgB"
|
||||
|
||||
_, err := formatGoImports([]byte(`
|
||||
package test
|
||||
|
||||
import (
|
||||
"image/jpeg"
|
||||
// for processing gif images
|
||||
"image/gif"
|
||||
)
|
||||
`))
|
||||
assert.ErrorIs(t, err, errInvalidCommentBetweenImports)
|
||||
}
|
@@ -2,7 +2,6 @@
|
||||
// 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
|
||||
@@ -11,6 +10,7 @@ import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -27,7 +27,7 @@ func needsUpdate(dir string, filename string) (bool, []byte) {
|
||||
needRegen = true
|
||||
}
|
||||
|
||||
oldHash, err := os.ReadFile(filename + ".hash")
|
||||
oldHash, err := ioutil.ReadFile(filename + ".hash")
|
||||
if err != nil {
|
||||
oldHash = []byte{}
|
||||
}
|
||||
@@ -58,15 +58,11 @@ func needsUpdate(dir string, filename string) (bool, []byte) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 4 {
|
||||
if len(os.Args) != 4 {
|
||||
log.Fatal("Insufficient number of arguments. Need: directory packageName filename")
|
||||
}
|
||||
|
||||
dir, packageName, filename := os.Args[1], os.Args[2], os.Args[3]
|
||||
var useGlobalModTime bool
|
||||
if len(os.Args) == 5 {
|
||||
useGlobalModTime, _ = strconv.ParseBool(os.Args[4])
|
||||
}
|
||||
|
||||
update, newHash := needsUpdate(dir, filename)
|
||||
|
||||
@@ -78,14 +74,13 @@ func main() {
|
||||
fmt.Printf("generating bindata for %s\n", packageName)
|
||||
var fsTemplates http.FileSystem = http.Dir(dir)
|
||||
err := vfsgen.Generate(fsTemplates, vfsgen.Options{
|
||||
PackageName: packageName,
|
||||
BuildTags: "bindata",
|
||||
VariableName: "Assets",
|
||||
Filename: filename,
|
||||
UseGlobalModTime: useGlobalModTime,
|
||||
PackageName: packageName,
|
||||
BuildTags: "bindata",
|
||||
VariableName: "Assets",
|
||||
Filename: filename,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("%v\n", err)
|
||||
}
|
||||
_ = os.WriteFile(filename+".hash", newHash, 0666)
|
||||
_ = ioutil.WriteFile(filename+".hash", newHash, 0666)
|
||||
}
|
||||
|
@@ -3,26 +3,23 @@
|
||||
// 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 (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -68,7 +65,7 @@ func main() {
|
||||
}
|
||||
|
||||
// write
|
||||
err = os.WriteFile(*flagOut, buf, 0644)
|
||||
err = ioutil.WriteFile(*flagOut, buf, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -97,7 +94,7 @@ func generate() ([]byte, error) {
|
||||
defer res.Body.Close()
|
||||
|
||||
// read all
|
||||
body, err := io.ReadAll(res.Body)
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -158,7 +155,7 @@ func generate() ([]byte, error) {
|
||||
|
||||
// write a JSON file to use with tribute (write before adding skin tones since we can't support them there yet)
|
||||
file, _ := json.Marshal(data)
|
||||
_ = os.WriteFile("assets/emoji.json", file, 0644)
|
||||
_ = ioutil.WriteFile("assets/emoji.json", file, 0644)
|
||||
|
||||
// Add skin tones to emoji that support it
|
||||
var (
|
||||
@@ -177,7 +174,7 @@ func generate() ([]byte, error) {
|
||||
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
|
||||
// have that modifer placed at this spot
|
||||
s = append(s, "")
|
||||
copy(s[2:], s[1:])
|
||||
s[1] = k
|
||||
|
@@ -1,4 +1,3 @@
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
@@ -9,6 +8,7 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -33,7 +33,7 @@ func main() {
|
||||
flag.StringVar(&githubApiToken, "token", "", "github api token")
|
||||
flag.Parse()
|
||||
|
||||
file, err := os.CreateTemp(os.TempDir(), prefix)
|
||||
file, err := ioutil.TempFile(os.TempDir(), prefix)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create temp file. %s", err)
|
||||
@@ -113,13 +113,13 @@ func main() {
|
||||
for dst, src := range filesToCopy {
|
||||
// Read all content of src to data
|
||||
src = path.Join(destination, src)
|
||||
data, err := os.ReadFile(src)
|
||||
data, err := ioutil.ReadFile(src)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read src file. %s", err)
|
||||
}
|
||||
// Write data to dst
|
||||
dst = path.Join(destination, dst)
|
||||
err = os.WriteFile(dst, data, 0644)
|
||||
err = ioutil.WriteFile(dst, data, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to write new file. %s", err)
|
||||
}
|
||||
|
@@ -1,13 +1,12 @@
|
||||
import imageminZopfli from 'imagemin-zopfli';
|
||||
import {optimize} from 'svgo';
|
||||
import {fabric} from 'fabric';
|
||||
import fs from 'fs';
|
||||
import {resolve, dirname} from 'path';
|
||||
import {fileURLToPath} from 'url';
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
const {readFile, writeFile} = fs.promises;
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const logoFile = resolve(__dirname, '../assets/logo.svg');
|
||||
const imageminZopfli = require('imagemin-zopfli');
|
||||
const {fabric} = require('fabric');
|
||||
const {DOMParser, XMLSerializer} = require('xmldom');
|
||||
const {readFile, writeFile} = require('fs').promises;
|
||||
const {resolve} = require('path');
|
||||
const Svgo = require('svgo');
|
||||
|
||||
function exit(err) {
|
||||
if (err) console.error(err);
|
||||
@@ -22,22 +21,42 @@ function loadSvg(svg) {
|
||||
});
|
||||
}
|
||||
|
||||
async function generate(svg, outputFile, {size, bg}) {
|
||||
if (outputFile.endsWith('.svg')) {
|
||||
const {data} = optimize(svg, {
|
||||
plugins: [
|
||||
'preset-default',
|
||||
'removeDimensions',
|
||||
{
|
||||
name: 'addAttributesToSVGElement',
|
||||
params: {attributes: [{width: size}, {height: size}]}
|
||||
async function generateSvgFavicon(svg, outputFile) {
|
||||
const svgo = new Svgo({
|
||||
plugins: [
|
||||
{removeDimensions: true},
|
||||
{
|
||||
addAttributesToSVGElement: {
|
||||
attributes: [
|
||||
{'width': '32'},
|
||||
{'height': '32'},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
await writeFile(outputFile, data);
|
||||
return;
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const {data} = await svgo.optimize(svg);
|
||||
await writeFile(outputFile, data);
|
||||
}
|
||||
|
||||
async function generate(svg, outputFile, {size, bg, removeDetail} = {}) {
|
||||
const parser = new DOMParser();
|
||||
const serializer = new XMLSerializer();
|
||||
const document = parser.parseFromString(svg);
|
||||
|
||||
if (removeDetail) {
|
||||
for (const el of Array.from(document.getElementsByTagName('g') || [])) {
|
||||
for (const attribute of Array.from(el.attributes || [])) {
|
||||
if (attribute.name === 'class' && attribute.value === 'detail-remove') {
|
||||
el.parentNode.removeChild(el);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svg = serializer.serializeToString(document);
|
||||
|
||||
const {objects, options} = await loadSvg(svg);
|
||||
const canvas = new fabric.Canvas();
|
||||
canvas.setDimensions({width: size, height: size});
|
||||
@@ -67,17 +86,15 @@ async function generate(svg, outputFile, {size, bg}) {
|
||||
}
|
||||
|
||||
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}),
|
||||
]);
|
||||
const svg = await readFile(resolve(__dirname, '../assets/logo.svg'), 'utf8');
|
||||
await generateSvgFavicon(svg, resolve(__dirname, '../public/img/favicon.svg'));
|
||||
await generate(svg, resolve(__dirname, '../public/img/gitea-lg.png'), {size: 880});
|
||||
await generate(svg, resolve(__dirname, '../public/img/gitea-512.png'), {size: 512});
|
||||
await generate(svg, resolve(__dirname, '../public/img/gitea-192.png'), {size: 192});
|
||||
await generate(svg, resolve(__dirname, '../public/img/gitea-sm.png'), {size: 120});
|
||||
await generate(svg, resolve(__dirname, '../public/img/avatar_default.png'), {size: 200});
|
||||
await generate(svg, resolve(__dirname, '../public/img/favicon.png'), {size: 180, removeDetail: true});
|
||||
await generate(svg, resolve(__dirname, '../public/img/apple-touch-icon.png'), {size: 180, bg: true});
|
||||
}
|
||||
|
||||
main().then(exit).catch(exit);
|
||||
|
@@ -1,4 +1,3 @@
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
@@ -9,6 +8,7 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -33,7 +33,7 @@ func main() {
|
||||
flag.StringVar(&githubApiToken, "token", "", "github api token")
|
||||
flag.Parse()
|
||||
|
||||
file, err := os.CreateTemp(os.TempDir(), prefix)
|
||||
file, err := ioutil.TempFile(os.TempDir(), prefix)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create temp file. %s", err)
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import fastGlob from 'fast-glob';
|
||||
import {optimize} from 'svgo';
|
||||
import {resolve, parse, dirname} from 'path';
|
||||
import fs from 'fs';
|
||||
import {fileURLToPath} from 'url';
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
const fastGlob = require('fast-glob');
|
||||
const Svgo = require('svgo');
|
||||
const {resolve, parse} = require('path');
|
||||
const {readFile, writeFile, mkdir} = require('fs').promises;
|
||||
|
||||
const {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');
|
||||
|
||||
@@ -14,32 +14,37 @@ function exit(err) {
|
||||
process.exit(err ? 1 : 0);
|
||||
}
|
||||
|
||||
async function processFile(file, {prefix, fullName} = {}) {
|
||||
let name;
|
||||
async function processFile(file, {prefix = ''} = {}) {
|
||||
let name = parse(file).name;
|
||||
if (prefix) name = `${prefix}-${name}`;
|
||||
if (prefix === 'octicon') name = name.replace(/-[0-9]+$/, ''); // chop of '-16' on octicons
|
||||
|
||||
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'), {
|
||||
const svgo = new Svgo({
|
||||
plugins: [
|
||||
{name: 'preset-default'},
|
||||
{name: 'removeXMLNS'},
|
||||
{name: 'removeDimensions'},
|
||||
{name: 'prefixIds', params: {prefix: () => name}},
|
||||
{name: 'addClassesToSVGElement', params: {classNames: ['svg', name]}},
|
||||
{name: 'addAttributesToSVGElement', params: {attributes: [{'width': '16'}, {'height': '16'}, {'aria-hidden': 'true'}]}},
|
||||
{removeXMLNS: true},
|
||||
{removeDimensions: true},
|
||||
{
|
||||
addClassesToSVGElement: {
|
||||
classNames: [
|
||||
'svg',
|
||||
name,
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
addAttributesToSVGElement: {
|
||||
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));
|
||||
const {data} = await svgo.optimize(await readFile(file, 'utf8'));
|
||||
await writeFile(resolve(outputDir, `${name}.svg`), data);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
@@ -47,11 +52,13 @@ async function main() {
|
||||
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'}),
|
||||
]);
|
||||
for (const file of glob('../node_modules/@primer/octicons/build/svg/*-16.svg')) {
|
||||
await processFile(file, {prefix: 'octicon'});
|
||||
}
|
||||
|
||||
for (const file of glob('../web_src/svg/*.svg')) {
|
||||
await processFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
main().then(exit).catch(exit);
|
||||
|
@@ -1,27 +0,0 @@
|
||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"code.gitea.io/gitea/build/codeformat"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) <= 1 {
|
||||
log.Fatalf("Usage: gitea-format-imports [files...]")
|
||||
}
|
||||
|
||||
for _, file := range os.Args[1:] {
|
||||
if err := codeformat.FormatGoImports(file); err != nil {
|
||||
log.Fatalf("can not format file %s, err=%v", file, err)
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,7 +6,6 @@
|
||||
// gocovmerge takes the results from multiple `go test -coverprofile` runs and
|
||||
// merges them into one profile
|
||||
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
@@ -109,7 +108,7 @@ func main() {
|
||||
for _, file := range flag.Args() {
|
||||
profiles, err := cover.ParseProfiles(file)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to parse profile '%s': %v", file, err)
|
||||
log.Fatalf("failed to parse profiles: %v", err)
|
||||
}
|
||||
for _, p := range profiles {
|
||||
merged = addProfile(merged, p)
|
||||
|
325
build/lint.go
Normal file
325
build/lint.go
Normal file
@@ -0,0 +1,325 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Copyright (c) 2018 Minko Gechev. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/mgechev/dots"
|
||||
"github.com/mgechev/revive/formatter"
|
||||
"github.com/mgechev/revive/lint"
|
||||
"github.com/mgechev/revive/rule"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/pelletier/go-toml"
|
||||
)
|
||||
|
||||
func fail(err string) {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var defaultRules = []lint.Rule{
|
||||
&rule.VarDeclarationsRule{},
|
||||
&rule.PackageCommentsRule{},
|
||||
&rule.DotImportsRule{},
|
||||
&rule.BlankImportsRule{},
|
||||
&rule.ExportedRule{},
|
||||
&rule.VarNamingRule{},
|
||||
&rule.IndentErrorFlowRule{},
|
||||
&rule.IfReturnRule{},
|
||||
&rule.RangeRule{},
|
||||
&rule.ErrorfRule{},
|
||||
&rule.ErrorNamingRule{},
|
||||
&rule.ErrorStringsRule{},
|
||||
&rule.ReceiverNamingRule{},
|
||||
&rule.IncrementDecrementRule{},
|
||||
&rule.ErrorReturnRule{},
|
||||
&rule.UnexportedReturnRule{},
|
||||
&rule.TimeNamingRule{},
|
||||
&rule.ContextKeysType{},
|
||||
&rule.ContextAsArgumentRule{},
|
||||
}
|
||||
|
||||
var allRules = append([]lint.Rule{
|
||||
&rule.ArgumentsLimitRule{},
|
||||
&rule.CyclomaticRule{},
|
||||
&rule.FileHeaderRule{},
|
||||
&rule.EmptyBlockRule{},
|
||||
&rule.SuperfluousElseRule{},
|
||||
&rule.ConfusingNamingRule{},
|
||||
&rule.GetReturnRule{},
|
||||
&rule.ModifiesParamRule{},
|
||||
&rule.ConfusingResultsRule{},
|
||||
&rule.DeepExitRule{},
|
||||
&rule.UnusedParamRule{},
|
||||
&rule.UnreachableCodeRule{},
|
||||
&rule.AddConstantRule{},
|
||||
&rule.FlagParamRule{},
|
||||
&rule.UnnecessaryStmtRule{},
|
||||
&rule.StructTagRule{},
|
||||
&rule.ModifiesValRecRule{},
|
||||
&rule.ConstantLogicalExprRule{},
|
||||
&rule.BoolLiteralRule{},
|
||||
&rule.RedefinesBuiltinIDRule{},
|
||||
&rule.ImportsBlacklistRule{},
|
||||
&rule.FunctionResultsLimitRule{},
|
||||
&rule.MaxPublicStructsRule{},
|
||||
&rule.RangeValInClosureRule{},
|
||||
&rule.RangeValAddress{},
|
||||
&rule.WaitGroupByValueRule{},
|
||||
&rule.AtomicRule{},
|
||||
&rule.EmptyLinesRule{},
|
||||
&rule.LineLengthLimitRule{},
|
||||
&rule.CallToGCRule{},
|
||||
&rule.DuplicatedImportsRule{},
|
||||
&rule.ImportShadowingRule{},
|
||||
&rule.BareReturnRule{},
|
||||
&rule.UnusedReceiverRule{},
|
||||
&rule.UnhandledErrorRule{},
|
||||
&rule.CognitiveComplexityRule{},
|
||||
&rule.StringOfIntRule{},
|
||||
}, defaultRules...)
|
||||
|
||||
var allFormatters = []lint.Formatter{
|
||||
&formatter.Stylish{},
|
||||
&formatter.Friendly{},
|
||||
&formatter.JSON{},
|
||||
&formatter.NDJSON{},
|
||||
&formatter.Default{},
|
||||
&formatter.Unix{},
|
||||
&formatter.Checkstyle{},
|
||||
&formatter.Plain{},
|
||||
}
|
||||
|
||||
func getFormatters() map[string]lint.Formatter {
|
||||
result := map[string]lint.Formatter{}
|
||||
for _, f := range allFormatters {
|
||||
result[f.Name()] = f
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func getLintingRules(config *lint.Config) []lint.Rule {
|
||||
rulesMap := map[string]lint.Rule{}
|
||||
for _, r := range allRules {
|
||||
rulesMap[r.Name()] = r
|
||||
}
|
||||
|
||||
lintingRules := []lint.Rule{}
|
||||
for name := range config.Rules {
|
||||
rule, ok := rulesMap[name]
|
||||
if !ok {
|
||||
fail("cannot find rule: " + name)
|
||||
}
|
||||
lintingRules = append(lintingRules, rule)
|
||||
}
|
||||
|
||||
return lintingRules
|
||||
}
|
||||
|
||||
func parseConfig(path string) *lint.Config {
|
||||
config := &lint.Config{}
|
||||
file, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
fail("cannot read the config file")
|
||||
}
|
||||
err = toml.Unmarshal(file, config)
|
||||
if err != nil {
|
||||
fail("cannot parse the config file: " + err.Error())
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
func normalizeConfig(config *lint.Config) {
|
||||
if config.Confidence == 0 {
|
||||
config.Confidence = 0.8
|
||||
}
|
||||
severity := config.Severity
|
||||
if severity != "" {
|
||||
for k, v := range config.Rules {
|
||||
if v.Severity == "" {
|
||||
v.Severity = severity
|
||||
}
|
||||
config.Rules[k] = v
|
||||
}
|
||||
for k, v := range config.Directives {
|
||||
if v.Severity == "" {
|
||||
v.Severity = severity
|
||||
}
|
||||
config.Directives[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getConfig() *lint.Config {
|
||||
config := defaultConfig()
|
||||
if configPath != "" {
|
||||
config = parseConfig(configPath)
|
||||
}
|
||||
normalizeConfig(config)
|
||||
return config
|
||||
}
|
||||
|
||||
func getFormatter() lint.Formatter {
|
||||
formatters := getFormatters()
|
||||
formatter := formatters["default"]
|
||||
if formatterName != "" {
|
||||
f, ok := formatters[formatterName]
|
||||
if !ok {
|
||||
fail("unknown formatter " + formatterName)
|
||||
}
|
||||
formatter = f
|
||||
}
|
||||
return formatter
|
||||
}
|
||||
|
||||
func buildDefaultConfigPath() string {
|
||||
var result string
|
||||
if homeDir, err := homedir.Dir(); err == nil {
|
||||
result = filepath.Join(homeDir, "revive.toml")
|
||||
if _, err := os.Stat(result); err != nil {
|
||||
result = ""
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func defaultConfig() *lint.Config {
|
||||
defaultConfig := lint.Config{
|
||||
Confidence: 0.0,
|
||||
Severity: lint.SeverityWarning,
|
||||
Rules: map[string]lint.RuleConfig{},
|
||||
}
|
||||
for _, r := range defaultRules {
|
||||
defaultConfig.Rules[r.Name()] = lint.RuleConfig{}
|
||||
}
|
||||
return &defaultConfig
|
||||
}
|
||||
|
||||
func normalizeSplit(strs []string) []string {
|
||||
res := []string{}
|
||||
for _, s := range strs {
|
||||
t := strings.Trim(s, " \t")
|
||||
if len(t) > 0 {
|
||||
res = append(res, t)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func getPackages() [][]string {
|
||||
globs := normalizeSplit(flag.Args())
|
||||
if len(globs) == 0 {
|
||||
globs = append(globs, ".")
|
||||
}
|
||||
|
||||
packages, err := dots.ResolvePackages(globs, normalizeSplit(excludePaths))
|
||||
if err != nil {
|
||||
fail(err.Error())
|
||||
}
|
||||
|
||||
return packages
|
||||
}
|
||||
|
||||
type arrayFlags []string
|
||||
|
||||
func (i *arrayFlags) String() string {
|
||||
return strings.Join([]string(*i), " ")
|
||||
}
|
||||
|
||||
func (i *arrayFlags) Set(value string) error {
|
||||
*i = append(*i, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
var configPath string
|
||||
var excludePaths arrayFlags
|
||||
var formatterName string
|
||||
var help bool
|
||||
|
||||
var originalUsage = flag.Usage
|
||||
|
||||
func init() {
|
||||
flag.Usage = func() {
|
||||
originalUsage()
|
||||
}
|
||||
// command line help strings
|
||||
const (
|
||||
configUsage = "path to the configuration TOML file, defaults to $HOME/revive.toml, if present (i.e. -config myconf.toml)"
|
||||
excludeUsage = "list of globs which specify files to be excluded (i.e. -exclude foo/...)"
|
||||
formatterUsage = "formatter to be used for the output (i.e. -formatter stylish)"
|
||||
)
|
||||
|
||||
defaultConfigPath := buildDefaultConfigPath()
|
||||
|
||||
flag.StringVar(&configPath, "config", defaultConfigPath, configUsage)
|
||||
flag.Var(&excludePaths, "exclude", excludeUsage)
|
||||
flag.StringVar(&formatterName, "formatter", "", formatterUsage)
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func main() {
|
||||
config := getConfig()
|
||||
formatter := getFormatter()
|
||||
packages := getPackages()
|
||||
|
||||
revive := lint.New(func(file string) ([]byte, error) {
|
||||
return ioutil.ReadFile(file)
|
||||
})
|
||||
|
||||
lintingRules := getLintingRules(config)
|
||||
|
||||
failures, err := revive.Lint(packages, lintingRules, *config)
|
||||
if err != nil {
|
||||
fail(err.Error())
|
||||
}
|
||||
|
||||
formatChan := make(chan lint.Failure)
|
||||
exitChan := make(chan bool)
|
||||
|
||||
var output string
|
||||
go (func() {
|
||||
output, err = formatter.Format(formatChan, *config)
|
||||
if err != nil {
|
||||
fail(err.Error())
|
||||
}
|
||||
exitChan <- true
|
||||
})()
|
||||
|
||||
exitCode := 0
|
||||
for f := range failures {
|
||||
if f.Confidence < config.Confidence {
|
||||
continue
|
||||
}
|
||||
if exitCode == 0 {
|
||||
exitCode = config.WarningCode
|
||||
}
|
||||
if c, ok := config.Rules[f.RuleName]; ok && c.Severity == lint.SeverityError {
|
||||
exitCode = config.ErrorCode
|
||||
}
|
||||
if c, ok := config.Directives[f.RuleName]; ok && c.Severity == lint.SeverityError {
|
||||
exitCode = config.ErrorCode
|
||||
}
|
||||
|
||||
formatChan <- f
|
||||
}
|
||||
|
||||
close(formatChan)
|
||||
<-exitChan
|
||||
if output != "" {
|
||||
fmt.Println(output)
|
||||
}
|
||||
|
||||
os.Exit(exitCode)
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if [ ! -f ./build/test-env-check.sh ]; then
|
||||
echo "${0} can only be executed in gitea source root directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
echo "check uid ..."
|
||||
|
||||
# the uid of gitea defined in "https://gitea.com/gitea/test-env" is 1000
|
||||
gitea_uid=$(id -u gitea)
|
||||
if [ "$gitea_uid" != "1000" ]; then
|
||||
echo "The uid of linux user 'gitea' is expected to be 1000, but it is $gitea_uid"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cur_uid=$(id -u)
|
||||
if [ "$cur_uid" != "0" -a "$cur_uid" != "$gitea_uid" ]; then
|
||||
echo "The uid of current linux user is expected to be 0 or $gitea_uid, but it is $cur_uid"
|
||||
exit 1
|
||||
fi
|
@@ -1,11 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if [ ! -f ./build/test-env-prepare.sh ]; then
|
||||
echo "${0} can only be executed in gitea source root directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "change the owner of files to gitea ..."
|
||||
chown -R gitea:gitea .
|
@@ -10,10 +10,10 @@ sed -i -r -e '/^[a-zA-Z0-9_.-]+[ ]*=[ ]*".*"$/ {
|
||||
}' ./options/locale/*.ini
|
||||
|
||||
# Remove translation under 25% of en_us
|
||||
baselines=$(wc -l "./options/locale_en-US.ini" | cut -d" " -f1)
|
||||
baselines=`wc -l "./options/locale_en-US.ini" | cut -d" " -f1`
|
||||
baselines=$((baselines / 4))
|
||||
for filename in ./options/locale/*.ini; do
|
||||
lines=$(wc -l "$filename" | cut -d" " -f1)
|
||||
lines=`wc -l "$filename" | cut -d" " -f1`
|
||||
if [ $lines -lt $baselines ]; then
|
||||
echo "Removing $filename: $lines/$baselines"
|
||||
rm "$filename"
|
||||
|
519
cmd/admin.go
519
cmd/admin.go
@@ -10,26 +10,16 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/auth/oauth2"
|
||||
"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/storage"
|
||||
auth_service "code.gitea.io/gitea/services/auth"
|
||||
"code.gitea.io/gitea/services/auth/source/oauth2"
|
||||
"code.gitea.io/gitea/services/auth/source/smtp"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
user_service "code.gitea.io/gitea/services/user"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
@@ -40,39 +30,16 @@ var (
|
||||
Name: "admin",
|
||||
Usage: "Command line interface to perform common administrative operations",
|
||||
Subcommands: []cli.Command{
|
||||
subcmdUser,
|
||||
subcmdCreateUser,
|
||||
subcmdChangePassword,
|
||||
subcmdRepoSyncReleases,
|
||||
subcmdRegenerate,
|
||||
subcmdAuth,
|
||||
subcmdSendMail,
|
||||
},
|
||||
}
|
||||
|
||||
subcmdUser = cli.Command{
|
||||
Name: "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",
|
||||
subcmdCreateUser = cli.Command{
|
||||
Name: "create-user",
|
||||
Usage: "Create a new user in database",
|
||||
Action: runCreateUser,
|
||||
Flags: []cli.Flag{
|
||||
@@ -116,7 +83,7 @@ var (
|
||||
},
|
||||
}
|
||||
|
||||
microcmdUserChangePassword = cli.Command{
|
||||
subcmdChangePassword = cli.Command{
|
||||
Name: "change-password",
|
||||
Usage: "Change a user's password",
|
||||
Action: runChangePassword,
|
||||
@@ -134,26 +101,6 @@ var (
|
||||
},
|
||||
}
|
||||
|
||||
microcmdUserDelete = cli.Command{
|
||||
Name: "delete",
|
||||
Usage: "Delete specific user by id, name or email",
|
||||
Flags: []cli.Flag{
|
||||
cli.Int64Flag{
|
||||
Name: "id",
|
||||
Usage: "ID of user of the user to delete",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "username,u",
|
||||
Usage: "Username of the user to delete",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "email,e",
|
||||
Usage: "Email of the user to delete",
|
||||
},
|
||||
},
|
||||
Action: runDeleteUser,
|
||||
}
|
||||
|
||||
subcmdRepoSyncReleases = cli.Command{
|
||||
Name: "repo-sync-releases",
|
||||
Usage: "Synchronize repository releases with tags",
|
||||
@@ -191,8 +138,6 @@ var (
|
||||
cmdAuthUpdateLdapBindDn,
|
||||
cmdAuthAddLdapSimpleAuth,
|
||||
cmdAuthUpdateLdapSimpleAuth,
|
||||
microcmdAuthAddSMTP,
|
||||
microcmdAuthUpdateSMTP,
|
||||
microcmdAuthList,
|
||||
microcmdAuthDelete,
|
||||
},
|
||||
@@ -293,45 +238,6 @@ var (
|
||||
Value: "",
|
||||
Usage: "Use a custom Email URL (option for GitHub)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "icon-url",
|
||||
Value: "",
|
||||
Usage: "Custom icon URL for OAuth2 login source",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "skip-local-2fa",
|
||||
Usage: "Set to true to skip local 2fa for users authenticated by this source",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "scopes",
|
||||
Value: nil,
|
||||
Usage: "Scopes to request when to authenticate against this OAuth2 source",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "required-claim-name",
|
||||
Value: "",
|
||||
Usage: "Claim name that has to be set to allow users to login with this source",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "required-claim-value",
|
||||
Value: "",
|
||||
Usage: "Claim value that has to be set to allow users to login with this source",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "group-claim-name",
|
||||
Value: "",
|
||||
Usage: "Claim name providing group names for this source",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "admin-group",
|
||||
Value: "",
|
||||
Usage: "Group Claim value for administrator users",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "restricted-group",
|
||||
Value: "",
|
||||
Usage: "Group Claim value for restricted users",
|
||||
},
|
||||
}
|
||||
|
||||
microcmdAuthUpdateOauth = cli.Command{
|
||||
@@ -347,94 +253,6 @@ var (
|
||||
Action: runAddOauth,
|
||||
Flags: oauthCLIFlags,
|
||||
}
|
||||
|
||||
subcmdSendMail = cli.Command{
|
||||
Name: "sendmail",
|
||||
Usage: "Send a message to all users",
|
||||
Action: runSendMail,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "title",
|
||||
Usage: `a title of a message`,
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "content",
|
||||
Usage: "a content of a message",
|
||||
Value: "",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "force,f",
|
||||
Usage: "A flag to bypass a confirmation step",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
smtpCLIFlags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "name",
|
||||
Value: "",
|
||||
Usage: "Application Name",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "auth-type",
|
||||
Value: "PLAIN",
|
||||
Usage: "SMTP Authentication Type (PLAIN/LOGIN/CRAM-MD5) default PLAIN",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "host",
|
||||
Value: "",
|
||||
Usage: "SMTP Host",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "port",
|
||||
Usage: "SMTP Port",
|
||||
},
|
||||
cli.BoolTFlag{
|
||||
Name: "force-smtps",
|
||||
Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.",
|
||||
},
|
||||
cli.BoolTFlag{
|
||||
Name: "skip-verify",
|
||||
Usage: "Skip TLS verify.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "helo-hostname",
|
||||
Value: "",
|
||||
Usage: "Hostname sent with HELO. Leave blank to send current hostname",
|
||||
},
|
||||
cli.BoolTFlag{
|
||||
Name: "disable-helo",
|
||||
Usage: "Disable SMTP helo.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "allowed-domains",
|
||||
Value: "",
|
||||
Usage: "Leave empty to allow all domains. Separate multiple domains with a comma (',')",
|
||||
},
|
||||
cli.BoolTFlag{
|
||||
Name: "skip-local-2fa",
|
||||
Usage: "Skip 2FA to log on.",
|
||||
},
|
||||
cli.BoolTFlag{
|
||||
Name: "active",
|
||||
Usage: "This Authentication Source is Activated.",
|
||||
},
|
||||
}
|
||||
|
||||
microcmdAuthAddSMTP = cli.Command{
|
||||
Name: "add-smtp",
|
||||
Usage: "Add new SMTP authentication source",
|
||||
Action: runAddSMTP,
|
||||
Flags: smtpCLIFlags,
|
||||
}
|
||||
|
||||
microcmdAuthUpdateSMTP = cli.Command{
|
||||
Name: "update-smtp",
|
||||
Usage: "Update existing SMTP authentication source",
|
||||
Action: runUpdateSMTP,
|
||||
Flags: append(smtpCLIFlags[:1], append([]cli.Flag{idFlag}, smtpCLIFlags[1:]...)...),
|
||||
}
|
||||
)
|
||||
|
||||
func runChangePassword(c *cli.Context) error {
|
||||
@@ -442,16 +260,9 @@ func runChangePassword(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(c.String("password")) < setting.MinPasswordLength {
|
||||
return fmt.Errorf("Password is not long enough. Needs to be at least %d", setting.MinPasswordLength)
|
||||
}
|
||||
|
||||
if !pwd.IsComplexEnough(c.String("password")) {
|
||||
return errors.New("Password does not meet complexity requirements")
|
||||
}
|
||||
@@ -463,15 +274,16 @@ func runChangePassword(c *cli.Context) error {
|
||||
return errors.New("The password you chose is on a list of stolen passwords previously exposed in public data breaches. Please try again with a different password.\nFor more details, see https://haveibeenpwned.com/Passwords")
|
||||
}
|
||||
uname := c.String("username")
|
||||
user, err := user_model.GetUserByName(uname)
|
||||
user, err := models.GetUserByName(uname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = user.SetPassword(c.String("password")); err != nil {
|
||||
if user.Salt, err = models.GetUserSalt(); err != nil {
|
||||
return err
|
||||
}
|
||||
user.HashPassword(c.String("password"))
|
||||
|
||||
if err = user_model.UpdateUserCols(db.DefaultContext, user, "passwd", "passwd_hash_algo", "salt"); err != nil {
|
||||
if err := models.UpdateUserCols(user, "passwd", "passwd_hash_algo", "salt"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -503,10 +315,7 @@ func runCreateUser(c *cli.Context) error {
|
||||
fmt.Fprintf(os.Stderr, "--name flag is deprecated. Use --username instead.\n")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -529,7 +338,7 @@ func runCreateUser(c *cli.Context) error {
|
||||
|
||||
// If this is the first user being created.
|
||||
// Take it as the admin and don't force a password update.
|
||||
if n := user_model.CountUsers(); n == 0 {
|
||||
if n := models.CountUsers(); n == 0 {
|
||||
changePassword = false
|
||||
}
|
||||
|
||||
@@ -537,7 +346,7 @@ func runCreateUser(c *cli.Context) error {
|
||||
changePassword = c.Bool("must-change-password")
|
||||
}
|
||||
|
||||
u := &user_model.User{
|
||||
u := &models.User{
|
||||
Name: username,
|
||||
Email: c.String("email"),
|
||||
Passwd: password,
|
||||
@@ -547,7 +356,7 @@ func runCreateUser(c *cli.Context) error {
|
||||
Theme: setting.UI.DefaultTheme,
|
||||
}
|
||||
|
||||
if err := user_model.CreateUser(u); err != nil {
|
||||
if err := models.CreateUser(u); err != nil {
|
||||
return fmt.Errorf("CreateUser: %v", err)
|
||||
}
|
||||
|
||||
@@ -568,93 +377,15 @@ func runCreateUser(c *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func runListUsers(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
users, err := user_model.GetAllUsers()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0)
|
||||
|
||||
if c.IsSet("admin") {
|
||||
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\n")
|
||||
for _, u := range users {
|
||||
if u.IsAdmin {
|
||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", u.ID, u.Name, u.Email, u.IsActive)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\n")
|
||||
for _, u := range users {
|
||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
w.Flush()
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func runDeleteUser(c *cli.Context) error {
|
||||
if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
|
||||
return fmt.Errorf("You must provide the id, username or email of a user to delete")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := storage.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var err error
|
||||
var user *user_model.User
|
||||
if c.IsSet("email") {
|
||||
user, err = user_model.GetUserByEmail(c.String("email"))
|
||||
} else if c.IsSet("username") {
|
||||
user, err = user_model.GetUserByName(c.String("username"))
|
||||
} else {
|
||||
user, err = user_model.GetUserByID(c.Int64("id"))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) {
|
||||
return fmt.Errorf("The user %s who has email %s does not match the provided username %s", user.Name, c.String("email"), c.String("username"))
|
||||
}
|
||||
|
||||
if c.IsSet("id") && user.ID != c.Int64("id") {
|
||||
return fmt.Errorf("The user %s does not match the provided id %d", user.Name, c.Int64("id"))
|
||||
}
|
||||
|
||||
return user_service.DeleteUser(user)
|
||||
}
|
||||
|
||||
func runRepoSyncReleases(_ *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
func runRepoSyncReleases(c *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: db.ListOptions{
|
||||
ListOptions: models.ListOptions{
|
||||
PageSize: models.RepositoryListDefaultPageSize,
|
||||
Page: page,
|
||||
},
|
||||
@@ -712,27 +443,21 @@ func getReleaseCount(id int64) (int64, error) {
|
||||
)
|
||||
}
|
||||
|
||||
func runRegenerateHooks(_ *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
func runRegenerateHooks(c *cli.Context) error {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
|
||||
return repo_module.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
|
||||
}
|
||||
|
||||
func runRegenerateKeys(_ *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
func runRegenerateKeys(c *cli.Context) error {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
return asymkey_model.RewriteAllPublicKeys()
|
||||
return models.RewriteAllPublicKeys()
|
||||
}
|
||||
|
||||
func parseOAuth2Config(c *cli.Context) *oauth2.Source {
|
||||
func parseOAuth2Config(c *cli.Context) *models.OAuth2Config {
|
||||
var customURLMapping *oauth2.CustomURLMapping
|
||||
if c.IsSet("use-custom-urls") {
|
||||
customURLMapping = &oauth2.CustomURLMapping{
|
||||
@@ -744,36 +469,25 @@ func parseOAuth2Config(c *cli.Context) *oauth2.Source {
|
||||
} else {
|
||||
customURLMapping = nil
|
||||
}
|
||||
return &oauth2.Source{
|
||||
return &models.OAuth2Config{
|
||||
Provider: c.String("provider"),
|
||||
ClientID: c.String("key"),
|
||||
ClientSecret: c.String("secret"),
|
||||
OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
|
||||
CustomURLMapping: customURLMapping,
|
||||
IconURL: c.String("icon-url"),
|
||||
SkipLocalTwoFA: c.Bool("skip-local-2fa"),
|
||||
Scopes: c.StringSlice("scopes"),
|
||||
RequiredClaimName: c.String("required-claim-name"),
|
||||
RequiredClaimValue: c.String("required-claim-value"),
|
||||
GroupClaimName: c.String("group-claim-name"),
|
||||
AdminGroup: c.String("admin-group"),
|
||||
RestrictedGroup: c.String("restricted-group"),
|
||||
}
|
||||
}
|
||||
|
||||
func runAddOauth(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return auth.CreateSource(&auth.Source{
|
||||
Type: auth.OAuth2,
|
||||
Name: c.String("name"),
|
||||
IsActive: true,
|
||||
Cfg: parseOAuth2Config(c),
|
||||
return models.CreateLoginSource(&models.LoginSource{
|
||||
Type: models.LoginOAuth2,
|
||||
Name: c.String("name"),
|
||||
IsActived: true,
|
||||
Cfg: parseOAuth2Config(c),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -782,19 +496,16 @@ func runUpdateOauth(c *cli.Context) error {
|
||||
return fmt.Errorf("--id flag is missing")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
source, err := auth.GetSourceByID(c.Int64("id"))
|
||||
source, err := models.GetLoginSourceByID(c.Int64("id"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oAuth2Config := source.Cfg.(*oauth2.Source)
|
||||
oAuth2Config := source.OAuth2()
|
||||
|
||||
if c.IsSet("name") {
|
||||
source.Name = c.String("name")
|
||||
@@ -816,32 +527,6 @@ func runUpdateOauth(c *cli.Context) error {
|
||||
oAuth2Config.OpenIDConnectAutoDiscoveryURL = c.String("auto-discover-url")
|
||||
}
|
||||
|
||||
if c.IsSet("icon-url") {
|
||||
oAuth2Config.IconURL = c.String("icon-url")
|
||||
}
|
||||
|
||||
if c.IsSet("scopes") {
|
||||
oAuth2Config.Scopes = c.StringSlice("scopes")
|
||||
}
|
||||
|
||||
if c.IsSet("required-claim-name") {
|
||||
oAuth2Config.RequiredClaimName = c.String("required-claim-name")
|
||||
|
||||
}
|
||||
if c.IsSet("required-claim-value") {
|
||||
oAuth2Config.RequiredClaimValue = c.String("required-claim-value")
|
||||
}
|
||||
|
||||
if c.IsSet("group-claim-name") {
|
||||
oAuth2Config.GroupClaimName = c.String("group-claim-name")
|
||||
}
|
||||
if c.IsSet("admin-group") {
|
||||
oAuth2Config.AdminGroup = c.String("admin-group")
|
||||
}
|
||||
if c.IsSet("restricted-group") {
|
||||
oAuth2Config.RestrictedGroup = c.String("restricted-group")
|
||||
}
|
||||
|
||||
// update custom URL mapping
|
||||
var customURLMapping = &oauth2.CustomURLMapping{}
|
||||
|
||||
@@ -870,130 +555,15 @@ func runUpdateOauth(c *cli.Context) error {
|
||||
oAuth2Config.CustomURLMapping = customURLMapping
|
||||
source.Cfg = oAuth2Config
|
||||
|
||||
return auth.UpdateSource(source)
|
||||
}
|
||||
|
||||
func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
|
||||
if c.IsSet("auth-type") {
|
||||
conf.Auth = c.String("auth-type")
|
||||
validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"}
|
||||
if !contains(validAuthTypes, strings.ToUpper(c.String("auth-type"))) {
|
||||
return errors.New("Auth must be one of PLAIN/LOGIN/CRAM-MD5")
|
||||
}
|
||||
conf.Auth = c.String("auth-type")
|
||||
}
|
||||
if c.IsSet("host") {
|
||||
conf.Host = c.String("host")
|
||||
}
|
||||
if c.IsSet("port") {
|
||||
conf.Port = c.Int("port")
|
||||
}
|
||||
if c.IsSet("allowed-domains") {
|
||||
conf.AllowedDomains = c.String("allowed-domains")
|
||||
}
|
||||
if c.IsSet("force-smtps") {
|
||||
conf.ForceSMTPS = c.BoolT("force-smtps")
|
||||
}
|
||||
if c.IsSet("skip-verify") {
|
||||
conf.SkipVerify = c.BoolT("skip-verify")
|
||||
}
|
||||
if c.IsSet("helo-hostname") {
|
||||
conf.HeloHostname = c.String("helo-hostname")
|
||||
}
|
||||
if c.IsSet("disable-helo") {
|
||||
conf.DisableHelo = c.BoolT("disable-helo")
|
||||
}
|
||||
if c.IsSet("skip-local-2fa") {
|
||||
conf.SkipLocalTwoFA = c.BoolT("skip-local-2fa")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runAddSMTP(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !c.IsSet("name") || len(c.String("name")) == 0 {
|
||||
return errors.New("name must be set")
|
||||
}
|
||||
if !c.IsSet("host") || len(c.String("host")) == 0 {
|
||||
return errors.New("host must be set")
|
||||
}
|
||||
if !c.IsSet("port") {
|
||||
return errors.New("port must be set")
|
||||
}
|
||||
var active = true
|
||||
if c.IsSet("active") {
|
||||
active = c.BoolT("active")
|
||||
}
|
||||
|
||||
var smtpConfig smtp.Source
|
||||
if err := parseSMTPConfig(c, &smtpConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If not set default to PLAIN
|
||||
if len(smtpConfig.Auth) == 0 {
|
||||
smtpConfig.Auth = "PLAIN"
|
||||
}
|
||||
|
||||
return auth.CreateSource(&auth.Source{
|
||||
Type: auth.SMTP,
|
||||
Name: c.String("name"),
|
||||
IsActive: active,
|
||||
Cfg: &smtpConfig,
|
||||
})
|
||||
}
|
||||
|
||||
func runUpdateSMTP(c *cli.Context) error {
|
||||
if !c.IsSet("id") {
|
||||
return fmt.Errorf("--id flag is missing")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
source, err := auth.GetSourceByID(c.Int64("id"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
smtpConfig := source.Cfg.(*smtp.Source)
|
||||
|
||||
if err := parseSMTPConfig(c, smtpConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.IsSet("name") {
|
||||
source.Name = c.String("name")
|
||||
}
|
||||
|
||||
if c.IsSet("active") {
|
||||
source.IsActive = c.BoolT("active")
|
||||
}
|
||||
|
||||
source.Cfg = smtpConfig
|
||||
|
||||
return auth.UpdateSource(source)
|
||||
return models.UpdateSource(source)
|
||||
}
|
||||
|
||||
func runListAuth(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authSources, err := auth.Sources()
|
||||
loginSources, err := models.LoginSources()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1012,8 +582,8 @@ func runListAuth(c *cli.Context) error {
|
||||
// loop through each source and print
|
||||
w := tabwriter.NewWriter(os.Stdout, c.Int("min-width"), c.Int("tab-width"), c.Int("padding"), padChar, flags)
|
||||
fmt.Fprintf(w, "ID\tName\tType\tEnabled\n")
|
||||
for _, source := range authSources {
|
||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, source.Type.String(), source.IsActive)
|
||||
for _, source := range loginSources {
|
||||
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, models.LoginNames[source.Type], source.IsActived)
|
||||
}
|
||||
w.Flush()
|
||||
|
||||
@@ -1025,17 +595,14 @@ func runDeleteAuth(c *cli.Context) error {
|
||||
return fmt.Errorf("--id flag is missing")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
source, err := auth.GetSourceByID(c.Int64("id"))
|
||||
source, err := models.GetLoginSourceByID(c.Int64("id"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return auth_service.DeleteSource(source)
|
||||
return models.DeleteSource(source)
|
||||
}
|
||||
|
@@ -5,22 +5,21 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/auth"
|
||||
"code.gitea.io/gitea/services/auth/source/ldap"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/auth/ldap"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
type (
|
||||
authService struct {
|
||||
initDB func(ctx context.Context) error
|
||||
createAuthSource func(*auth.Source) error
|
||||
updateAuthSource func(*auth.Source) error
|
||||
getAuthSourceByID func(id int64) (*auth.Source, error)
|
||||
initDB func() error
|
||||
createLoginSource func(loginSource *models.LoginSource) error
|
||||
updateLoginSource func(loginSource *models.LoginSource) error
|
||||
getLoginSourceByID func(id int64) (*models.LoginSource, error)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -90,14 +89,6 @@ var (
|
||||
Name: "public-ssh-key-attribute",
|
||||
Usage: "The attribute of the user’s LDAP record containing the user’s public ssh key.",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "skip-local-2fa",
|
||||
Usage: "Set to true to skip local 2fa for users authenticated by this source",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "avatar-attribute",
|
||||
Usage: "The attribute of the user’s LDAP record containing the user’s avatar.",
|
||||
},
|
||||
}
|
||||
|
||||
ldapBindDnCLIFlags = append(commonLdapCLIFlags,
|
||||
@@ -168,106 +159,99 @@ var (
|
||||
// newAuthService creates a service with default functions.
|
||||
func newAuthService() *authService {
|
||||
return &authService{
|
||||
initDB: initDB,
|
||||
createAuthSource: auth.CreateSource,
|
||||
updateAuthSource: auth.UpdateSource,
|
||||
getAuthSourceByID: auth.GetSourceByID,
|
||||
initDB: initDB,
|
||||
createLoginSource: models.CreateLoginSource,
|
||||
updateLoginSource: models.UpdateSource,
|
||||
getLoginSourceByID: models.GetLoginSourceByID,
|
||||
}
|
||||
}
|
||||
|
||||
// parseAuthSource assigns values on authSource according to command line flags.
|
||||
func parseAuthSource(c *cli.Context, authSource *auth.Source) {
|
||||
// parseLoginSource assigns values on loginSource according to command line flags.
|
||||
func parseLoginSource(c *cli.Context, loginSource *models.LoginSource) {
|
||||
if c.IsSet("name") {
|
||||
authSource.Name = c.String("name")
|
||||
loginSource.Name = c.String("name")
|
||||
}
|
||||
if c.IsSet("not-active") {
|
||||
authSource.IsActive = !c.Bool("not-active")
|
||||
loginSource.IsActived = !c.Bool("not-active")
|
||||
}
|
||||
if c.IsSet("synchronize-users") {
|
||||
authSource.IsSyncEnabled = c.Bool("synchronize-users")
|
||||
loginSource.IsSyncEnabled = c.Bool("synchronize-users")
|
||||
}
|
||||
}
|
||||
|
||||
// parseLdapConfig assigns values on config according to command line flags.
|
||||
func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
|
||||
func parseLdapConfig(c *cli.Context, config *models.LDAPConfig) error {
|
||||
if c.IsSet("name") {
|
||||
config.Name = c.String("name")
|
||||
config.Source.Name = c.String("name")
|
||||
}
|
||||
if c.IsSet("host") {
|
||||
config.Host = c.String("host")
|
||||
config.Source.Host = c.String("host")
|
||||
}
|
||||
if c.IsSet("port") {
|
||||
config.Port = c.Int("port")
|
||||
config.Source.Port = c.Int("port")
|
||||
}
|
||||
if c.IsSet("security-protocol") {
|
||||
p, ok := findLdapSecurityProtocolByName(c.String("security-protocol"))
|
||||
if !ok {
|
||||
return fmt.Errorf("Unknown security protocol name: %s", c.String("security-protocol"))
|
||||
}
|
||||
config.SecurityProtocol = p
|
||||
config.Source.SecurityProtocol = p
|
||||
}
|
||||
if c.IsSet("skip-tls-verify") {
|
||||
config.SkipVerify = c.Bool("skip-tls-verify")
|
||||
config.Source.SkipVerify = c.Bool("skip-tls-verify")
|
||||
}
|
||||
if c.IsSet("bind-dn") {
|
||||
config.BindDN = c.String("bind-dn")
|
||||
config.Source.BindDN = c.String("bind-dn")
|
||||
}
|
||||
if c.IsSet("user-dn") {
|
||||
config.UserDN = c.String("user-dn")
|
||||
config.Source.UserDN = c.String("user-dn")
|
||||
}
|
||||
if c.IsSet("bind-password") {
|
||||
config.BindPassword = c.String("bind-password")
|
||||
config.Source.BindPassword = c.String("bind-password")
|
||||
}
|
||||
if c.IsSet("user-search-base") {
|
||||
config.UserBase = c.String("user-search-base")
|
||||
config.Source.UserBase = c.String("user-search-base")
|
||||
}
|
||||
if c.IsSet("username-attribute") {
|
||||
config.AttributeUsername = c.String("username-attribute")
|
||||
config.Source.AttributeUsername = c.String("username-attribute")
|
||||
}
|
||||
if c.IsSet("firstname-attribute") {
|
||||
config.AttributeName = c.String("firstname-attribute")
|
||||
config.Source.AttributeName = c.String("firstname-attribute")
|
||||
}
|
||||
if c.IsSet("surname-attribute") {
|
||||
config.AttributeSurname = c.String("surname-attribute")
|
||||
config.Source.AttributeSurname = c.String("surname-attribute")
|
||||
}
|
||||
if c.IsSet("email-attribute") {
|
||||
config.AttributeMail = c.String("email-attribute")
|
||||
config.Source.AttributeMail = c.String("email-attribute")
|
||||
}
|
||||
if c.IsSet("attributes-in-bind") {
|
||||
config.AttributesInBind = c.Bool("attributes-in-bind")
|
||||
config.Source.AttributesInBind = c.Bool("attributes-in-bind")
|
||||
}
|
||||
if c.IsSet("public-ssh-key-attribute") {
|
||||
config.AttributeSSHPublicKey = c.String("public-ssh-key-attribute")
|
||||
}
|
||||
if c.IsSet("avatar-attribute") {
|
||||
config.AttributeAvatar = c.String("avatar-attribute")
|
||||
config.Source.AttributeSSHPublicKey = c.String("public-ssh-key-attribute")
|
||||
}
|
||||
if c.IsSet("page-size") {
|
||||
config.SearchPageSize = uint32(c.Uint("page-size"))
|
||||
config.Source.SearchPageSize = uint32(c.Uint("page-size"))
|
||||
}
|
||||
if c.IsSet("user-filter") {
|
||||
config.Filter = c.String("user-filter")
|
||||
config.Source.Filter = c.String("user-filter")
|
||||
}
|
||||
if c.IsSet("admin-filter") {
|
||||
config.AdminFilter = c.String("admin-filter")
|
||||
config.Source.AdminFilter = c.String("admin-filter")
|
||||
}
|
||||
if c.IsSet("restricted-filter") {
|
||||
config.RestrictedFilter = c.String("restricted-filter")
|
||||
config.Source.RestrictedFilter = c.String("restricted-filter")
|
||||
}
|
||||
if c.IsSet("allow-deactivate-all") {
|
||||
config.AllowDeactivateAll = c.Bool("allow-deactivate-all")
|
||||
config.Source.AllowDeactivateAll = c.Bool("allow-deactivate-all")
|
||||
}
|
||||
if c.IsSet("skip-local-2fa") {
|
||||
config.SkipLocalTwoFA = c.Bool("skip-local-2fa")
|
||||
}
|
||||
|
||||
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 {
|
||||
for i, n := range models.SecurityProtocolNames {
|
||||
if strings.EqualFold(name, n) {
|
||||
return i, true
|
||||
}
|
||||
@@ -275,23 +259,23 @@ func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// getAuthSource gets the login source by its id defined in the command line flags.
|
||||
// getLoginSource gets the login source by its id defined in the command line flags.
|
||||
// It returns an error if the id is not set, does not match any source or if the source is not of expected type.
|
||||
func (a *authService) getAuthSource(c *cli.Context, authType auth.Type) (*auth.Source, error) {
|
||||
func (a *authService) getLoginSource(c *cli.Context, loginType models.LoginType) (*models.LoginSource, error) {
|
||||
if err := argsSet(c, "id"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authSource, err := a.getAuthSourceByID(c.Int64("id"))
|
||||
loginSource, err := a.getLoginSourceByID(c.Int64("id"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if authSource.Type != authType {
|
||||
return nil, fmt.Errorf("Invalid authentication type. expected: %s, actual: %s", authType.String(), authSource.Type.String())
|
||||
if loginSource.Type != loginType {
|
||||
return nil, fmt.Errorf("Invalid authentication type. expected: %s, actual: %s", models.LoginNames[loginType], models.LoginNames[loginSource.Type])
|
||||
}
|
||||
|
||||
return authSource, nil
|
||||
return loginSource, nil
|
||||
}
|
||||
|
||||
// addLdapBindDn adds a new LDAP via Bind DN authentication source.
|
||||
@@ -300,49 +284,45 @@ func (a *authService) addLdapBindDn(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := a.initDB(ctx); err != nil {
|
||||
if err := a.initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authSource := &auth.Source{
|
||||
Type: auth.LDAP,
|
||||
IsActive: true, // active by default
|
||||
Cfg: &ldap.Source{
|
||||
Enabled: true, // always true
|
||||
loginSource := &models.LoginSource{
|
||||
Type: models.LoginLDAP,
|
||||
IsActived: true, // active by default
|
||||
Cfg: &models.LDAPConfig{
|
||||
Source: &ldap.Source{
|
||||
Enabled: true, // always true
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
parseAuthSource(c, authSource)
|
||||
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
||||
parseLoginSource(c, loginSource)
|
||||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.createAuthSource(authSource)
|
||||
return a.createLoginSource(loginSource)
|
||||
}
|
||||
|
||||
// updateLdapBindDn updates a new LDAP via Bind DN authentication source.
|
||||
func (a *authService) updateLdapBindDn(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := a.initDB(ctx); err != nil {
|
||||
if err := a.initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authSource, err := a.getAuthSource(c, auth.LDAP)
|
||||
loginSource, err := a.getLoginSource(c, models.LoginLDAP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parseAuthSource(c, authSource)
|
||||
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
||||
parseLoginSource(c, loginSource)
|
||||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.updateAuthSource(authSource)
|
||||
return a.updateLoginSource(loginSource)
|
||||
}
|
||||
|
||||
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
|
||||
@@ -351,47 +331,43 @@ func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := a.initDB(ctx); err != nil {
|
||||
if err := a.initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authSource := &auth.Source{
|
||||
Type: auth.DLDAP,
|
||||
IsActive: true, // active by default
|
||||
Cfg: &ldap.Source{
|
||||
Enabled: true, // always true
|
||||
loginSource := &models.LoginSource{
|
||||
Type: models.LoginDLDAP,
|
||||
IsActived: true, // active by default
|
||||
Cfg: &models.LDAPConfig{
|
||||
Source: &ldap.Source{
|
||||
Enabled: true, // always true
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
parseAuthSource(c, authSource)
|
||||
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
||||
parseLoginSource(c, loginSource)
|
||||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.createAuthSource(authSource)
|
||||
return a.createLoginSource(loginSource)
|
||||
}
|
||||
|
||||
// updateLdapBindDn updates a new LDAP (simple auth) authentication source.
|
||||
func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := a.initDB(ctx); err != nil {
|
||||
if err := a.initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authSource, err := a.getAuthSource(c, auth.DLDAP)
|
||||
loginSource, err := a.getLoginSource(c, models.LoginDLDAP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parseAuthSource(c, authSource)
|
||||
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
|
||||
parseLoginSource(c, loginSource)
|
||||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.updateAuthSource(authSource)
|
||||
return a.updateLoginSource(loginSource)
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
64
cmd/cmd.go
64
cmd/cmd.go
@@ -7,16 +7,10 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
@@ -38,59 +32,17 @@ func argsSet(c *cli.Context, args ...string) error {
|
||||
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 initDB(ctx context.Context) error {
|
||||
setting.LoadFromExisting()
|
||||
func initDBDisableConsole(disableConsole bool) error {
|
||||
setting.NewContext()
|
||||
setting.InitDBConfig()
|
||||
setting.NewXORMLogService(false)
|
||||
|
||||
if setting.Database.Type == "" {
|
||||
log.Fatal(`Database settings are missing from the configuration file: %q.
|
||||
Ensure you are running in the correct environment or set the correct configuration file with -c.
|
||||
If this is the intended configuration file complete the [database] section.`, setting.CustomConf)
|
||||
}
|
||||
if err := db.InitEngine(ctx); err != nil {
|
||||
return fmt.Errorf("unable to initialise the database using the configuration in %q. Error: %v", setting.CustomConf, err)
|
||||
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
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@ package cmd
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
@@ -23,25 +23,22 @@ var CmdConvert = cli.Command{
|
||||
}
|
||||
|
||||
func runConvert(ctx *cli.Context) error {
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(stdCtx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("AppPath: %s", setting.AppPath)
|
||||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Info("Custom path: %s", setting.CustomPath)
|
||||
log.Info("Log path: %s", setting.LogRootPath)
|
||||
log.Info("Configuration file: %s", setting.CustomConf)
|
||||
log.Trace("AppPath: %s", setting.AppPath)
|
||||
log.Trace("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Trace("Custom path: %s", setting.CustomPath)
|
||||
log.Trace("Log path: %s", setting.LogRootPath)
|
||||
setting.InitDBConfig()
|
||||
|
||||
if !setting.Database.UseMySQL {
|
||||
fmt.Println("This command can only be used with a MySQL database")
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := db.ConvertUtf8ToUtf8mb4(); err != nil {
|
||||
if err := models.ConvertUtf8ToUtf8mb4(); err != nil {
|
||||
log.Fatal("Failed to convert database from utf8 to utf8mb4: %v", err)
|
||||
return err
|
||||
}
|
||||
|
65
cmd/docs.go
65
cmd/docs.go
@@ -1,65 +0,0 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// CmdDocs represents the available docs sub-command.
|
||||
var CmdDocs = cli.Command{
|
||||
Name: "docs",
|
||||
Usage: "Output CLI documentation",
|
||||
Description: "A command to output Gitea's CLI documentation, optionally to a file.",
|
||||
Action: runDocs,
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "man",
|
||||
Usage: "Output man pages instead",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "output, o",
|
||||
Usage: "Path to output to instead of stdout (will overwrite if exists)",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func runDocs(ctx *cli.Context) error {
|
||||
docs, err := ctx.App.ToMarkdown()
|
||||
if ctx.Bool("man") {
|
||||
docs, err = ctx.App.ToMan()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ctx.Bool("man") {
|
||||
// Clean up markdown. The following bug was fixed in v2, but is present in v1.
|
||||
// It affects markdown output (even though the issue is referring to man pages)
|
||||
// https://github.com/urfave/cli/issues/1040
|
||||
firstHashtagIndex := strings.Index(docs, "#")
|
||||
|
||||
if firstHashtagIndex > 0 {
|
||||
docs = docs[firstHashtagIndex:]
|
||||
}
|
||||
}
|
||||
|
||||
out := os.Stdout
|
||||
if ctx.String("output") != "" {
|
||||
fi, err := os.Create(ctx.String("output"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fi.Close()
|
||||
out = fi
|
||||
}
|
||||
|
||||
_, err = fmt.Fprintln(out, docs)
|
||||
return err
|
||||
}
|
576
cmd/doctor.go
576
cmd/doctor.go
@@ -5,21 +5,30 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
golog "log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/migrations"
|
||||
"code.gitea.io/gitea/modules/doctor"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/options"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CmdDoctor represents the available doctor sub-command.
|
||||
@@ -53,10 +62,6 @@ var CmdDoctor = cli.Command{
|
||||
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,
|
||||
@@ -81,13 +86,85 @@ You should back-up your database before doing this and ensure that your database
|
||||
Action: runRecreateTable,
|
||||
}
|
||||
|
||||
type check struct {
|
||||
title string
|
||||
name string
|
||||
isDefault bool
|
||||
f func(ctx *cli.Context) ([]string, error)
|
||||
abortIfFailed bool
|
||||
skipDatabaseInit bool
|
||||
}
|
||||
|
||||
// checklist represents list for all checks
|
||||
var checklist = []check{
|
||||
{
|
||||
// NOTE: this check should be the first in the list
|
||||
title: "Check paths and basic configuration",
|
||||
name: "paths",
|
||||
isDefault: true,
|
||||
f: runDoctorPathInfo,
|
||||
abortIfFailed: true,
|
||||
skipDatabaseInit: true,
|
||||
},
|
||||
{
|
||||
title: "Check Database Version",
|
||||
name: "check-db-version",
|
||||
isDefault: true,
|
||||
f: runDoctorCheckDBVersion,
|
||||
abortIfFailed: false,
|
||||
},
|
||||
{
|
||||
title: "Check consistency of database",
|
||||
name: "check-db-consistency",
|
||||
isDefault: false,
|
||||
f: runDoctorCheckDBConsistency,
|
||||
},
|
||||
{
|
||||
title: "Check if OpenSSH authorized_keys file is up-to-date",
|
||||
name: "authorized_keys",
|
||||
isDefault: true,
|
||||
f: runDoctorAuthorizedKeys,
|
||||
},
|
||||
{
|
||||
title: "Check if SCRIPT_TYPE is available",
|
||||
name: "script-type",
|
||||
isDefault: false,
|
||||
f: runDoctorScriptType,
|
||||
},
|
||||
{
|
||||
title: "Check if hook files are up-to-date and executable",
|
||||
name: "hooks",
|
||||
isDefault: false,
|
||||
f: runDoctorHooks,
|
||||
},
|
||||
{
|
||||
title: "Recalculate merge bases",
|
||||
name: "recalculate_merge_bases",
|
||||
isDefault: false,
|
||||
f: runDoctorPRMergeBase,
|
||||
},
|
||||
{
|
||||
title: "Recalculate Stars number for all user",
|
||||
name: "recalculate_stars_number",
|
||||
isDefault: false,
|
||||
f: runDoctorUserStarNum,
|
||||
},
|
||||
{
|
||||
title: "Enable push options",
|
||||
name: "enable-push-options",
|
||||
isDefault: false,
|
||||
f: runDoctorEnablePushOptions,
|
||||
},
|
||||
// more checks please append here
|
||||
}
|
||||
|
||||
func runRecreateTable(ctx *cli.Context) error {
|
||||
// Redirect the default golog to here
|
||||
golog.SetFlags(0)
|
||||
golog.SetPrefix("")
|
||||
golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT)))
|
||||
|
||||
setting.LoadFromExisting()
|
||||
setting.NewContext()
|
||||
setting.InitDBConfig()
|
||||
|
||||
setting.EnableXORMLog = ctx.Bool("debug")
|
||||
@@ -95,10 +172,7 @@ func runRecreateTable(ctx *cli.Context) error {
|
||||
setting.Cfg.Section("log").Key("XORM").SetValue(",")
|
||||
|
||||
setting.NewXORMLogService(!ctx.Bool("debug"))
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := db.InitEngine(stdCtx); err != nil {
|
||||
if err := models.SetEngine(); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.")
|
||||
return nil
|
||||
@@ -110,13 +184,13 @@ func runRecreateTable(ctx *cli.Context) error {
|
||||
names = append(names, args.Get(i))
|
||||
}
|
||||
|
||||
beans, err := db.NamesToBean(names...)
|
||||
beans, err := models.NamesToBean(names...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
recreateTables := migrations.RecreateTables(beans...)
|
||||
|
||||
return db.InitEngineWithMigration(context.Background(), func(x *xorm.Engine) error {
|
||||
return models.NewEngine(context.Background(), func(x *xorm.Engine) error {
|
||||
if err := migrations.EnsureUpToDate(x); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -126,28 +200,21 @@ func runRecreateTable(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
func runDoctor(ctx *cli.Context) error {
|
||||
|
||||
// Silence the default loggers
|
||||
log.DelNamedLogger("console")
|
||||
log.DelNamedLogger(log.DEFAULT)
|
||||
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
// 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))
|
||||
log.NewLogger(1000, "doctor", "console", `{"level":"NONE","stacktracelevel":"NONE","colorize":"%t"}`)
|
||||
} else if logFile == "-" {
|
||||
log.NewLogger(1000, "doctor", "console", fmt.Sprintf(`{"level":"trace","stacktracelevel":"NONE","colorize":%t}`, colorize))
|
||||
log.NewLogger(1000, "doctor", "console", `{"level":"trace","stacktracelevel":"NONE"}`)
|
||||
} else {
|
||||
log.NewLogger(1000, "doctor", "file", fmt.Sprintf(`{"filename":%q,"level":"trace","stacktracelevel":"NONE"}`, logFile))
|
||||
}
|
||||
@@ -158,24 +225,24 @@ func runDoctor(ctx *cli.Context) error {
|
||||
golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT)))
|
||||
|
||||
if ctx.IsSet("list") {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 8, 0, '\t', 0)
|
||||
_, _ = w.Write([]byte("Default\tName\tTitle\n"))
|
||||
for _, check := range doctor.Checks {
|
||||
if check.IsDefault {
|
||||
for _, check := range checklist {
|
||||
if check.isDefault {
|
||||
_, _ = w.Write([]byte{'*'})
|
||||
}
|
||||
_, _ = w.Write([]byte{'\t'})
|
||||
_, _ = w.Write([]byte(check.Name))
|
||||
_, _ = w.Write([]byte(check.name))
|
||||
_, _ = w.Write([]byte{'\t'})
|
||||
_, _ = w.Write([]byte(check.Title))
|
||||
_, _ = w.Write([]byte(check.title))
|
||||
_, _ = w.Write([]byte{'\n'})
|
||||
}
|
||||
return w.Flush()
|
||||
}
|
||||
|
||||
var checks []*doctor.Check
|
||||
var checks []check
|
||||
if ctx.Bool("all") {
|
||||
checks = doctor.Checks
|
||||
checks = checklist
|
||||
} else if ctx.IsSet("run") {
|
||||
addDefault := ctx.Bool("default")
|
||||
names := ctx.StringSlice("run")
|
||||
@@ -183,37 +250,452 @@ func runDoctor(ctx *cli.Context) error {
|
||||
names[i] = strings.ToLower(strings.TrimSpace(name))
|
||||
}
|
||||
|
||||
for _, check := range doctor.Checks {
|
||||
if addDefault && check.IsDefault {
|
||||
for _, check := range checklist {
|
||||
if addDefault && check.isDefault {
|
||||
checks = append(checks, check)
|
||||
continue
|
||||
}
|
||||
for _, name := range names {
|
||||
if name == check.Name {
|
||||
if name == check.name {
|
||||
checks = append(checks, check)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, check := range doctor.Checks {
|
||||
if check.IsDefault {
|
||||
for _, check := range checklist {
|
||||
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
|
||||
dbIsInit := false
|
||||
|
||||
for i, check := range checks {
|
||||
if !dbIsInit && !check.skipDatabaseInit {
|
||||
// Only open database after the most basic configuration check
|
||||
setting.EnableXORMLog = false
|
||||
if err := initDBDisableConsole(true); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.")
|
||||
return nil
|
||||
}
|
||||
dbIsInit = true
|
||||
}
|
||||
fmt.Println("[", i+1, "]", check.title)
|
||||
messages, err := check.f(ctx)
|
||||
for _, message := range messages {
|
||||
fmt.Println("-", message)
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
if check.abortIfFailed {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
fmt.Println("OK.")
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runDoctorPathInfo(ctx *cli.Context) ([]string, error) {
|
||||
|
||||
res := make([]string, 0, 10)
|
||||
|
||||
if fi, err := os.Stat(setting.CustomConf); err != nil || !fi.Mode().IsRegular() {
|
||||
res = append(res, fmt.Sprintf("Failed to find configuration file at '%s'.", setting.CustomConf))
|
||||
res = append(res, fmt.Sprintf("If you've never ran Gitea yet, this is normal and '%s' will be created for you on first run.", setting.CustomConf))
|
||||
res = append(res, "Otherwise check that you are running this command from the correct path and/or provide a `--config` parameter.")
|
||||
return res, fmt.Errorf("can't proceed without a configuration file")
|
||||
}
|
||||
|
||||
logger := log.GetLogger("doctorouter")
|
||||
defer logger.Close()
|
||||
return doctor.RunChecks(stdCtx, logger, ctx.Bool("fix"), checks)
|
||||
setting.NewContext()
|
||||
|
||||
fail := false
|
||||
|
||||
check := func(name, path string, is_dir, required, is_write bool) {
|
||||
res = append(res, fmt.Sprintf("%-25s '%s'", name+":", path))
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) && ctx.Bool("fix") && is_dir {
|
||||
if err := os.MkdirAll(path, 0777); err != nil {
|
||||
res = append(res, fmt.Sprintf(" ERROR: %v", err))
|
||||
fail = true
|
||||
return
|
||||
}
|
||||
fi, err = os.Stat(path)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if required {
|
||||
res = append(res, fmt.Sprintf(" ERROR: %v", err))
|
||||
fail = true
|
||||
return
|
||||
}
|
||||
res = append(res, fmt.Sprintf(" NOTICE: not accessible (%v)", err))
|
||||
return
|
||||
}
|
||||
|
||||
if is_dir && !fi.IsDir() {
|
||||
res = append(res, " ERROR: not a directory")
|
||||
fail = true
|
||||
return
|
||||
} else if !is_dir && !fi.Mode().IsRegular() {
|
||||
res = append(res, " ERROR: not a regular file")
|
||||
fail = true
|
||||
} else if is_write {
|
||||
if err := runDoctorWritableDir(path); err != nil {
|
||||
res = append(res, fmt.Sprintf(" ERROR: not writable: %v", err))
|
||||
fail = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note print paths inside quotes to make any leading/trailing spaces evident
|
||||
check("Configuration File Path", setting.CustomConf, false, true, false)
|
||||
check("Repository Root Path", setting.RepoRootPath, true, true, true)
|
||||
check("Data Root Path", setting.AppDataPath, true, true, true)
|
||||
check("Custom File Root Path", setting.CustomPath, true, false, false)
|
||||
check("Work directory", setting.AppWorkPath, true, true, false)
|
||||
check("Log Root Path", setting.LogRootPath, true, true, true)
|
||||
|
||||
if options.IsDynamic() {
|
||||
// Do not check/report on StaticRootPath if data is embedded in Gitea (-tags bindata)
|
||||
check("Static File Root Path", setting.StaticRootPath, true, true, false)
|
||||
}
|
||||
|
||||
if fail {
|
||||
return res, fmt.Errorf("please check your configuration file and try again")
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func runDoctorWritableDir(path string) error {
|
||||
// There's no platform-independent way of checking if a directory is writable
|
||||
// https://stackoverflow.com/questions/20026320/how-to-tell-if-folder-exists-and-is-writable
|
||||
|
||||
tmpFile, err := ioutil.TempFile(path, "doctors-order")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := util.Remove(tmpFile.Name()); err != nil {
|
||||
fmt.Printf("Warning: can't remove temporary file: '%s'\n", tmpFile.Name())
|
||||
}
|
||||
tmpFile.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
const tplCommentPrefix = `# gitea public key`
|
||||
|
||||
func runDoctorAuthorizedKeys(ctx *cli.Context) ([]string, error) {
|
||||
if setting.SSH.StartBuiltinServer || !setting.SSH.CreateAuthorizedKeysFile {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
|
||||
f, err := os.Open(fPath)
|
||||
if err != nil {
|
||||
if ctx.Bool("fix") {
|
||||
return []string{fmt.Sprintf("Error whilst opening authorized_keys: %v. Attempting regeneration", err)}, models.RewriteAllPublicKeys()
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
linesInAuthorizedKeys := map[string]bool{}
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, tplCommentPrefix) {
|
||||
continue
|
||||
}
|
||||
linesInAuthorizedKeys[line] = true
|
||||
}
|
||||
f.Close()
|
||||
|
||||
// now we regenerate and check if there are any lines missing
|
||||
regenerated := &bytes.Buffer{}
|
||||
if err := models.RegeneratePublicKeys(regenerated); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scanner = bufio.NewScanner(regenerated)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, tplCommentPrefix) {
|
||||
continue
|
||||
}
|
||||
if ok := linesInAuthorizedKeys[line]; ok {
|
||||
continue
|
||||
}
|
||||
if ctx.Bool("fix") {
|
||||
return []string{"authorized_keys is out of date, attempting regeneration"}, models.RewriteAllPublicKeys()
|
||||
}
|
||||
return nil, fmt.Errorf(`authorized_keys is out of date and should be regenerated with "gitea admin regenerate keys" or "gitea doctor --run authorized_keys --fix"`)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func runDoctorCheckDBVersion(ctx *cli.Context) ([]string, error) {
|
||||
if err := models.NewEngine(context.Background(), migrations.EnsureUpToDate); err != nil {
|
||||
if ctx.Bool("fix") {
|
||||
return []string{fmt.Sprintf("WARN: Got Error %v during ensure up to date", err), "Attempting to migrate to the latest DB version to fix this."}, models.NewEngine(context.Background(), migrations.Migrate)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func iterateRepositories(each func(*models.Repository) ([]string, error)) ([]string, error) {
|
||||
results := []string{}
|
||||
err := models.Iterate(
|
||||
models.DefaultDBContext(),
|
||||
new(models.Repository),
|
||||
builder.Gt{"id": 0},
|
||||
func(idx int, bean interface{}) error {
|
||||
res, err := each(bean.(*models.Repository))
|
||||
results = append(results, res...)
|
||||
return err
|
||||
},
|
||||
)
|
||||
return results, err
|
||||
}
|
||||
|
||||
func iteratePRs(repo *models.Repository, each func(*models.Repository, *models.PullRequest) ([]string, error)) ([]string, error) {
|
||||
results := []string{}
|
||||
err := models.Iterate(
|
||||
models.DefaultDBContext(),
|
||||
new(models.PullRequest),
|
||||
builder.Eq{"base_repo_id": repo.ID},
|
||||
func(idx int, bean interface{}) error {
|
||||
res, err := each(repo, bean.(*models.PullRequest))
|
||||
results = append(results, res...)
|
||||
return err
|
||||
},
|
||||
)
|
||||
return results, err
|
||||
}
|
||||
|
||||
func runDoctorHooks(ctx *cli.Context) ([]string, error) {
|
||||
// Need to iterate across all of the repositories
|
||||
return iterateRepositories(func(repo *models.Repository) ([]string, error) {
|
||||
results, err := repository.CheckDelegateHooks(repo.RepoPath())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(results) > 0 && ctx.Bool("fix") {
|
||||
return []string{fmt.Sprintf("regenerated hooks for %s", repo.FullName())}, repository.CreateDelegateHooks(repo.RepoPath())
|
||||
}
|
||||
|
||||
return results, nil
|
||||
})
|
||||
}
|
||||
|
||||
func runDoctorPRMergeBase(ctx *cli.Context) ([]string, error) {
|
||||
numRepos := 0
|
||||
numPRs := 0
|
||||
numPRsUpdated := 0
|
||||
results, err := iterateRepositories(func(repo *models.Repository) ([]string, error) {
|
||||
numRepos++
|
||||
return iteratePRs(repo, func(repo *models.Repository, pr *models.PullRequest) ([]string, error) {
|
||||
numPRs++
|
||||
results := []string{}
|
||||
pr.BaseRepo = repo
|
||||
repoPath := repo.RepoPath()
|
||||
|
||||
oldMergeBase := pr.MergeBase
|
||||
|
||||
if !pr.HasMerged {
|
||||
var err error
|
||||
pr.MergeBase, err = git.NewCommand("merge-base", "--", pr.BaseBranch, pr.GetGitRefName()).RunInDir(repoPath)
|
||||
if err != nil {
|
||||
var err2 error
|
||||
pr.MergeBase, err2 = git.NewCommand("rev-parse", git.BranchPrefix+pr.BaseBranch).RunInDir(repoPath)
|
||||
if err2 != nil {
|
||||
results = append(results, fmt.Sprintf("WARN: Unable to get merge base for PR ID %d, #%d onto %s in %s/%s", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name))
|
||||
log.Error("Unable to get merge base for PR ID %d, Index %d in %s/%s. Error: %v & %v", pr.ID, pr.Index, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err, err2)
|
||||
return results, nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parentsString, err := git.NewCommand("rev-list", "--parents", "-n", "1", pr.MergedCommitID).RunInDir(repoPath)
|
||||
if err != nil {
|
||||
results = append(results, fmt.Sprintf("WARN: Unable to get parents for merged PR ID %d, #%d onto %s in %s/%s", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name))
|
||||
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err)
|
||||
return results, nil
|
||||
}
|
||||
parents := strings.Split(strings.TrimSpace(parentsString), " ")
|
||||
if len(parents) < 2 {
|
||||
return results, nil
|
||||
}
|
||||
|
||||
args := append([]string{"merge-base", "--"}, parents[1:]...)
|
||||
args = append(args, pr.GetGitRefName())
|
||||
|
||||
pr.MergeBase, err = git.NewCommand(args...).RunInDir(repoPath)
|
||||
if err != nil {
|
||||
results = append(results, fmt.Sprintf("WARN: Unable to get merge base for merged PR ID %d, #%d onto %s in %s/%s", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name))
|
||||
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err)
|
||||
return results, nil
|
||||
}
|
||||
}
|
||||
pr.MergeBase = strings.TrimSpace(pr.MergeBase)
|
||||
if pr.MergeBase != oldMergeBase {
|
||||
if ctx.Bool("fix") {
|
||||
if err := pr.UpdateCols("merge_base"); err != nil {
|
||||
return results, err
|
||||
}
|
||||
} else {
|
||||
results = append(results, fmt.Sprintf("#%d onto %s in %s/%s: MergeBase should be %s but is %s", pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, oldMergeBase, pr.MergeBase))
|
||||
}
|
||||
numPRsUpdated++
|
||||
}
|
||||
return results, nil
|
||||
})
|
||||
})
|
||||
|
||||
if ctx.Bool("fix") {
|
||||
results = append(results, fmt.Sprintf("%d PR mergebases updated of %d PRs total in %d repos", numPRsUpdated, numPRs, numRepos))
|
||||
} else {
|
||||
if numPRsUpdated > 0 && err == nil {
|
||||
return results, fmt.Errorf("%d PRs with incorrect mergebases of %d PRs total in %d repos", numPRsUpdated, numPRs, numRepos)
|
||||
}
|
||||
results = append(results, fmt.Sprintf("%d PRs with incorrect mergebases of %d PRs total in %d repos", numPRsUpdated, numPRs, numRepos))
|
||||
}
|
||||
|
||||
return results, err
|
||||
}
|
||||
|
||||
func runDoctorUserStarNum(ctx *cli.Context) ([]string, error) {
|
||||
return nil, models.DoctorUserStarNum()
|
||||
}
|
||||
|
||||
func runDoctorScriptType(ctx *cli.Context) ([]string, error) {
|
||||
path, err := exec.LookPath(setting.ScriptType)
|
||||
if err != nil {
|
||||
return []string{fmt.Sprintf("ScriptType %s is not on the current PATH", setting.ScriptType)}, err
|
||||
}
|
||||
return []string{fmt.Sprintf("ScriptType %s is on the current PATH at %s", setting.ScriptType, path)}, nil
|
||||
}
|
||||
|
||||
func runDoctorCheckDBConsistency(ctx *cli.Context) ([]string, error) {
|
||||
var results []string
|
||||
|
||||
// make sure DB version is uptodate
|
||||
if err := models.NewEngine(context.Background(), migrations.EnsureUpToDate); err != nil {
|
||||
return nil, fmt.Errorf("model version on the database does not match the current Gitea version. Model consistency will not be checked until the database is upgraded")
|
||||
}
|
||||
|
||||
//find labels without existing repo or org
|
||||
count, err := models.CountOrphanedLabels()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count > 0 {
|
||||
if ctx.Bool("fix") {
|
||||
if err = models.DeleteOrphanedLabels(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, fmt.Sprintf("%d labels without existing repository/organisation deleted", count))
|
||||
} else {
|
||||
results = append(results, fmt.Sprintf("%d labels without existing repository/organisation", count))
|
||||
}
|
||||
}
|
||||
|
||||
//find issues without existing repository
|
||||
count, err = models.CountOrphanedIssues()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count > 0 {
|
||||
if ctx.Bool("fix") {
|
||||
if err = models.DeleteOrphanedIssues(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, fmt.Sprintf("%d issues without existing repository deleted", count))
|
||||
} else {
|
||||
results = append(results, fmt.Sprintf("%d issues without existing repository", count))
|
||||
}
|
||||
}
|
||||
|
||||
//find pulls without existing issues
|
||||
count, err = models.CountOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count > 0 {
|
||||
if ctx.Bool("fix") {
|
||||
if err = models.DeleteOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, fmt.Sprintf("%d pull requests without existing issue deleted", count))
|
||||
} else {
|
||||
results = append(results, fmt.Sprintf("%d pull requests without existing issue", count))
|
||||
}
|
||||
}
|
||||
|
||||
//find tracked times without existing issues/pulls
|
||||
count, err = models.CountOrphanedObjects("tracked_time", "issue", "tracked_time.issue_id=issue.id")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count > 0 {
|
||||
if ctx.Bool("fix") {
|
||||
if err = models.DeleteOrphanedObjects("tracked_time", "issue", "tracked_time.issue_id=issue.id"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, fmt.Sprintf("%d tracked times without existing issue deleted", count))
|
||||
} else {
|
||||
results = append(results, fmt.Sprintf("%d tracked times without existing issue", count))
|
||||
}
|
||||
}
|
||||
|
||||
count, err = models.CountNullArchivedRepository()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count > 0 {
|
||||
if ctx.Bool("fix") {
|
||||
updatedCount, err := models.FixNullArchivedRepository()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, fmt.Sprintf("%d repositories with null is_archived updated", updatedCount))
|
||||
} else {
|
||||
results = append(results, fmt.Sprintf("%d repositories with null is_archived", count))
|
||||
}
|
||||
}
|
||||
|
||||
//ToDo: function to recalc all counters
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func runDoctorEnablePushOptions(ctx *cli.Context) ([]string, error) {
|
||||
numRepos := 0
|
||||
_, err := iterateRepositories(func(repo *models.Repository) ([]string, error) {
|
||||
numRepos++
|
||||
r, err := git.OpenRepository(repo.RepoPath())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
if ctx.Bool("fix") {
|
||||
_, err := git.NewCommand("config", "receive.advertisePushOptions", "true").RunInDir(r.Path)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
var prefix string
|
||||
if !ctx.Bool("fix") {
|
||||
prefix = "DRY RUN: "
|
||||
}
|
||||
return []string{fmt.Sprintf("%sEnabled push options for %d repositories.", prefix, numRepos)}, err
|
||||
}
|
||||
|
129
cmd/dump.go
129
cmd/dump.go
@@ -6,26 +6,28 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"gitea.com/go-chi/session"
|
||||
"gitea.com/macaron/session"
|
||||
archiver "github.com/mholt/archiver/v3"
|
||||
"github.com/unknwon/com"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func addFile(w archiver.Writer, filePath, absPath string, verbose bool) error {
|
||||
func addFile(w archiver.Writer, filePath string, absPath string, verbose bool) error {
|
||||
if verbose {
|
||||
log.Info("Adding file %s\n", filePath)
|
||||
}
|
||||
@@ -48,7 +50,39 @@ func addFile(w archiver.Writer, filePath, absPath string, verbose bool) error {
|
||||
})
|
||||
}
|
||||
|
||||
func isSubdir(upper, lower string) (bool, error) {
|
||||
func addRecursive(w archiver.Writer, dirPath string, absPath string, verbose bool) error {
|
||||
if verbose {
|
||||
log.Info("Adding dir %s\n", dirPath)
|
||||
}
|
||||
dir, err := os.Open(absPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not open directory %s: %s", absPath, err)
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
files, err := dir.Readdir(0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to list files in %s: %s", absPath, err)
|
||||
}
|
||||
|
||||
if err := addFile(w, dirPath, absPath, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, fileInfo := range files {
|
||||
if fileInfo.IsDir() {
|
||||
err = addRecursive(w, filepath.Join(dirPath, fileInfo.Name()), filepath.Join(absPath, fileInfo.Name()), verbose)
|
||||
} else {
|
||||
err = addFile(w, filepath.Join(dirPath, fileInfo.Name()), filepath.Join(absPath, fileInfo.Name()), verbose)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isSubdir(upper string, lower string) (bool, error) {
|
||||
if relPath, err := filepath.Rel(upper, lower); err != nil {
|
||||
return false, err
|
||||
} else if relPath == "." || !strings.HasPrefix(relPath, ".") {
|
||||
@@ -86,7 +120,7 @@ func (o outputType) String() string {
|
||||
}
|
||||
|
||||
var outputTypeEnum = &outputType{
|
||||
Enum: []string{"zip", "rar", "tar", "sz", "tar.gz", "tar.xz", "tar.bz2", "tar.br", "tar.lz4"},
|
||||
Enum: []string{"zip", "tar", "tar.gz", "tar.xz", "tar.bz2"},
|
||||
Default: "zip",
|
||||
}
|
||||
|
||||
@@ -124,18 +158,6 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
|
||||
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,
|
||||
@@ -152,19 +174,14 @@ func fatal(format string, args ...interface{}) {
|
||||
func runDump(ctx *cli.Context) error {
|
||||
var file *os.File
|
||||
fileName := ctx.String("file")
|
||||
outType := ctx.String("type")
|
||||
if fileName == "-" {
|
||||
file = os.Stdout
|
||||
err := log.DelLogger("console")
|
||||
if err != nil {
|
||||
fatal("Deleting default logger failed. Can not write to stdout: %v", err)
|
||||
}
|
||||
} else {
|
||||
fileName = strings.TrimSuffix(fileName, path.Ext(fileName))
|
||||
fileName += "." + outType
|
||||
}
|
||||
setting.LoadFromExisting()
|
||||
|
||||
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)
|
||||
@@ -178,10 +195,7 @@ func runDump(ctx *cli.Context) error {
|
||||
}
|
||||
setting.NewServices() // cannot access session settings otherwise
|
||||
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
err := db.InitEngine(stdCtx)
|
||||
err := models.SetEngine()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -198,12 +212,8 @@ func runDump(ctx *cli.Context) error {
|
||||
}
|
||||
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))
|
||||
@@ -224,13 +234,11 @@ func runDump(ctx *cli.Context) error {
|
||||
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 {
|
||||
if err := addRecursive(w, "repos", setting.RepoRootPath, 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 {
|
||||
if err := storage.LFS.IterateObjects(func(objPath string, object storage.Object) error {
|
||||
info, err := object.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -253,7 +261,7 @@ func runDump(ctx *cli.Context) error {
|
||||
fatal("Path does not exist: %s", tmpDir)
|
||||
}
|
||||
|
||||
dbDump, err := os.CreateTemp(tmpDir, "gitea-db.sql")
|
||||
dbDump, err := ioutil.TempFile(tmpDir, "gitea-db.sql")
|
||||
if err != nil {
|
||||
fatal("Failed to create tmp file: %v", err)
|
||||
}
|
||||
@@ -270,7 +278,7 @@ func runDump(ctx *cli.Context) error {
|
||||
log.Info("Dumping database...")
|
||||
}
|
||||
|
||||
if err := db.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
|
||||
if err := models.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
|
||||
fatal("Failed to dump database: %v", err)
|
||||
}
|
||||
|
||||
@@ -285,28 +293,20 @@ func runDump(ctx *cli.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
customDir, err := os.Stat(setting.CustomPath)
|
||||
if err == nil && customDir.IsDir() {
|
||||
if is, _ := isSubdir(setting.AppDataPath, setting.CustomPath); !is {
|
||||
if err := addRecursive(w, "custom", setting.CustomPath, verbose); err != nil {
|
||||
fatal("Failed to include custom: %v", err)
|
||||
}
|
||||
} else {
|
||||
log.Info("Custom dir %s doesn't exist, skipped", setting.CustomPath)
|
||||
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 {
|
||||
log.Error("Unable to check if %s exists. Error: %v", setting.AppDataPath, err)
|
||||
}
|
||||
if isExist {
|
||||
if com.IsExist(setting.AppDataPath) {
|
||||
log.Info("Packing data directory...%s", setting.AppDataPath)
|
||||
|
||||
var excludes []string
|
||||
@@ -322,15 +322,12 @@ func runDump(ctx *cli.Context) error {
|
||||
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") {
|
||||
log.Info("Skip dumping attachment data")
|
||||
} else if err := storage.Attachments.IterateObjects(func(objPath string, object storage.Object) error {
|
||||
if err := storage.Attachments.IterateObjects(func(objPath string, object storage.Object) error {
|
||||
info, err := object.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -352,15 +349,9 @@ func runDump(ctx *cli.Context) error {
|
||||
// yet or not.
|
||||
if ctx.IsSet("skip-log") && ctx.Bool("skip-log") {
|
||||
log.Info("Skip dumping log files")
|
||||
} else {
|
||||
isExist, err := util.IsExist(setting.LogRootPath)
|
||||
if err != nil {
|
||||
log.Error("Unable to check if %s exists. Error: %v", setting.LogRootPath, err)
|
||||
}
|
||||
if isExist {
|
||||
if err := addRecursiveExclude(w, "log", setting.LogRootPath, []string{absFileName}, verbose); err != nil {
|
||||
fatal("Failed to include log: %v", err)
|
||||
}
|
||||
} else if com.IsExist(setting.LogRootPath) {
|
||||
if err := addRecursive(w, "log", setting.LogRootPath, verbose); err != nil {
|
||||
fatal("Failed to include log: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
165
cmd/dump_repo.go
165
cmd/dump_repo.go
@@ -1,165 +0,0 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/convert"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
base "code.gitea.io/gitea/modules/migration"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/services/migrations"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// CmdDumpRepository represents the available dump repository sub-command.
|
||||
var CmdDumpRepository = cli.Command{
|
||||
Name: "dump-repo",
|
||||
Usage: "Dump the repository from git/github/gitea/gitlab",
|
||||
Description: "This is a command for dumping the repository data.",
|
||||
Action: runDumpRepository,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "git_service",
|
||||
Value: "",
|
||||
Usage: "Git service, git, github, gitea, gitlab. If clone_addr could be recognized, this could be ignored.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "repo_dir, r",
|
||||
Value: "./data",
|
||||
Usage: "Repository dir path to store the data",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "clone_addr",
|
||||
Value: "",
|
||||
Usage: "The URL will be clone, currently could be a git/github/gitea/gitlab http/https URL",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "auth_username",
|
||||
Value: "",
|
||||
Usage: "The username to visit the clone_addr",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "auth_password",
|
||||
Value: "",
|
||||
Usage: "The password to visit the clone_addr",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "auth_token",
|
||||
Value: "",
|
||||
Usage: "The personal token to visit the clone_addr",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "owner_name",
|
||||
Value: "",
|
||||
Usage: "The data will be stored on a directory with owner name if not empty",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "repo_name",
|
||||
Value: "",
|
||||
Usage: "The data will be stored on a directory with repository name if not empty",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "units",
|
||||
Value: "",
|
||||
Usage: `Which items will be migrated, one or more units should be separated as comma.
|
||||
wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func runDumpRepository(ctx *cli.Context) error {
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(stdCtx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("AppPath: %s", setting.AppPath)
|
||||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Info("Custom path: %s", setting.CustomPath)
|
||||
log.Info("Log path: %s", setting.LogRootPath)
|
||||
log.Info("Configuration file: %s", setting.CustomConf)
|
||||
|
||||
var (
|
||||
serviceType structs.GitServiceType
|
||||
cloneAddr = ctx.String("clone_addr")
|
||||
serviceStr = ctx.String("git_service")
|
||||
)
|
||||
|
||||
if strings.HasPrefix(strings.ToLower(cloneAddr), "https://github.com/") {
|
||||
serviceStr = "github"
|
||||
} else if strings.HasPrefix(strings.ToLower(cloneAddr), "https://gitlab.com/") {
|
||||
serviceStr = "gitlab"
|
||||
} else if strings.HasPrefix(strings.ToLower(cloneAddr), "https://gitea.com/") {
|
||||
serviceStr = "gitea"
|
||||
}
|
||||
if serviceStr == "" {
|
||||
return errors.New("git_service missed or clone_addr cannot be recognized")
|
||||
}
|
||||
serviceType = convert.ToGitServiceType(serviceStr)
|
||||
|
||||
var opts = base.MigrateOptions{
|
||||
GitServiceType: serviceType,
|
||||
CloneAddr: cloneAddr,
|
||||
AuthUsername: ctx.String("auth_username"),
|
||||
AuthPassword: ctx.String("auth_password"),
|
||||
AuthToken: ctx.String("auth_token"),
|
||||
RepoName: ctx.String("repo_name"),
|
||||
}
|
||||
|
||||
if len(ctx.String("units")) == 0 {
|
||||
opts.Wiki = true
|
||||
opts.Issues = true
|
||||
opts.Milestones = true
|
||||
opts.Labels = true
|
||||
opts.Releases = true
|
||||
opts.Comments = true
|
||||
opts.PullRequests = true
|
||||
opts.ReleaseAssets = true
|
||||
} else {
|
||||
units := strings.Split(ctx.String("units"), ",")
|
||||
for _, unit := range units {
|
||||
switch strings.ToLower(unit) {
|
||||
case "wiki":
|
||||
opts.Wiki = true
|
||||
case "issues":
|
||||
opts.Issues = true
|
||||
case "milestones":
|
||||
opts.Milestones = true
|
||||
case "labels":
|
||||
opts.Labels = true
|
||||
case "releases":
|
||||
opts.Releases = true
|
||||
case "release_assets":
|
||||
opts.ReleaseAssets = true
|
||||
case "comments":
|
||||
opts.Comments = true
|
||||
case "pull_requests":
|
||||
opts.PullRequests = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := migrations.DumpRepository(
|
||||
context.Background(),
|
||||
ctx.String("repo_dir"),
|
||||
ctx.String("owner_name"),
|
||||
opts,
|
||||
); err != nil {
|
||||
log.Fatal("Failed to dump repository: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("Dump finished!!!")
|
||||
|
||||
return nil
|
||||
}
|
@@ -2,7 +2,6 @@
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build bindata
|
||||
// +build bindata
|
||||
|
||||
package cmd
|
||||
@@ -20,7 +19,6 @@ import (
|
||||
"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"
|
||||
@@ -115,7 +113,7 @@ func initEmbeddedExtractor(c *cli.Context) error {
|
||||
log.DelNamedLogger(log.DEFAULT)
|
||||
|
||||
// Read configuration file
|
||||
setting.LoadAllowEmpty()
|
||||
setting.NewContext()
|
||||
|
||||
pats, err := getPatterns(c.Args())
|
||||
if err != nil {
|
||||
@@ -273,7 +271,7 @@ func extractAsset(d string, a asset, overwrite, rename bool) error {
|
||||
} 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 {
|
||||
if err := os.Rename(dest, dest+".bak"); err != nil {
|
||||
return fmt.Errorf("Error creating backup for %s: %v", dest, err)
|
||||
}
|
||||
// Attempt to respect file permissions mask (even if user:group will be set anew)
|
||||
|
@@ -2,7 +2,6 @@
|
||||
// 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
|
||||
|
@@ -71,7 +71,7 @@ func runGenerateInternalToken(c *cli.Context) error {
|
||||
}
|
||||
|
||||
func runGenerateLfsJwtSecret(c *cli.Context) error {
|
||||
JWTSecretBase64, err := generate.NewJwtSecretBase64()
|
||||
JWTSecretBase64, err := generate.NewJwtSecret()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
408
cmd/hook.go
408
cmd/hook.go
@@ -38,7 +38,6 @@ var (
|
||||
subcmdHookPreReceive,
|
||||
subcmdHookUpdate,
|
||||
subcmdHookPostReceive,
|
||||
subcmdHookProcReceive,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -75,18 +74,6 @@ var (
|
||||
},
|
||||
},
|
||||
}
|
||||
// Note: new hook since git 2.29
|
||||
subcmdHookProcReceive = cli.Command{
|
||||
Name: "proc-receive",
|
||||
Usage: "Delegate proc-receive Git hook",
|
||||
Description: "This command should only be called by Git",
|
||||
Action: runHookProcReceive,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type delayWriter struct {
|
||||
@@ -165,22 +152,21 @@ 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 setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
||||
return fail(`Rejecting changes as Gitea environment not set.
|
||||
fail(`Rejecting changes as Gitea environment not set.
|
||||
If you are pushing over SSH you must push with a key managed by
|
||||
Gitea or set your environment appropriately.`, "")
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// the environment is set by serv command
|
||||
isWiki := os.Getenv(models.EnvRepoIsWiki) == "true"
|
||||
// the environment setted on serv command
|
||||
isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
|
||||
username := os.Getenv(models.EnvRepoUsername)
|
||||
reponame := os.Getenv(models.EnvRepoName)
|
||||
userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||
@@ -193,7 +179,7 @@ Gitea or set your environment appropriately.`, "")
|
||||
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
||||
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
||||
GitPushOptions: pushOptions(),
|
||||
PullRequestID: prID,
|
||||
ProtectedBranchID: prID,
|
||||
IsDeployKey: isDeployKey,
|
||||
}
|
||||
|
||||
@@ -218,11 +204,6 @@ Gitea or set your environment appropriately.`, "")
|
||||
}
|
||||
}
|
||||
|
||||
supportProcRecive := false
|
||||
if git.CheckGitVersionAtLeast("2.29") == nil {
|
||||
supportProcRecive = true
|
||||
}
|
||||
|
||||
for scanner.Scan() {
|
||||
// TODO: support news feeds for wiki
|
||||
if isWiki {
|
||||
@@ -240,10 +221,8 @@ Gitea or set your environment appropriately.`, "")
|
||||
total++
|
||||
lastline++
|
||||
|
||||
// If the ref is a branch or tag, check if it's protected
|
||||
// if supportProcRecive all ref should be checked because
|
||||
// permission check was delayed
|
||||
if supportProcRecive || strings.HasPrefix(refFullName, git.BranchPrefix) || strings.HasPrefix(refFullName, git.TagPrefix) {
|
||||
// If the ref is a branch, check if it's protected
|
||||
if strings.HasPrefix(refFullName, git.BranchPrefix) {
|
||||
oldCommitIDs[count] = oldCommitID
|
||||
newCommitIDs[count] = newCommitID
|
||||
refFullNames[count] = refFullName
|
||||
@@ -251,19 +230,19 @@ Gitea or set your environment appropriately.`, "")
|
||||
fmt.Fprintf(out, "*")
|
||||
|
||||
if count >= hookBatchSize {
|
||||
fmt.Fprintf(out, " Checking %d references\n", count)
|
||||
fmt.Fprintf(out, " Checking %d branches\n", count)
|
||||
|
||||
hookOptions.OldCommitIDs = oldCommitIDs
|
||||
hookOptions.NewCommitIDs = newCommitIDs
|
||||
hookOptions.RefFullNames = refFullNames
|
||||
statusCode, msg := private.HookPreReceive(ctx, username, reponame, hookOptions)
|
||||
statusCode, msg := private.HookPreReceive(username, reponame, hookOptions)
|
||||
switch statusCode {
|
||||
case http.StatusOK:
|
||||
// no-op
|
||||
case http.StatusInternalServerError:
|
||||
return fail("Internal Server Error", msg)
|
||||
fail("Internal Server Error", msg)
|
||||
default:
|
||||
return fail(msg, "")
|
||||
fail(msg, "")
|
||||
}
|
||||
count = 0
|
||||
lastline = 0
|
||||
@@ -282,17 +261,18 @@ Gitea or set your environment appropriately.`, "")
|
||||
hookOptions.NewCommitIDs = newCommitIDs[:count]
|
||||
hookOptions.RefFullNames = refFullNames[:count]
|
||||
|
||||
fmt.Fprintf(out, " Checking %d references\n", count)
|
||||
fmt.Fprintf(out, " Checking %d branches\n", count)
|
||||
|
||||
statusCode, msg := private.HookPreReceive(ctx, username, reponame, hookOptions)
|
||||
statusCode, msg := private.HookPreReceive(username, reponame, hookOptions)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("Internal Server Error", msg)
|
||||
fail("Internal Server Error", msg)
|
||||
case http.StatusForbidden:
|
||||
return fail(msg, "")
|
||||
fail(msg, "")
|
||||
}
|
||||
} else if lastline > 0 {
|
||||
fmt.Fprintf(out, "\n")
|
||||
lastline = 0
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "Checked %d references in total\n", total)
|
||||
@@ -305,11 +285,8 @@ func runHookUpdate(c *cli.Context) error {
|
||||
}
|
||||
|
||||
func runHookPostReceive(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
// First of all run update-server-info no matter what
|
||||
if _, err := git.NewCommandContext(ctx, "update-server-info").Run(); err != nil {
|
||||
if _, err := git.NewCommand("update-server-info").Run(); err != nil {
|
||||
return fmt.Errorf("Failed to call 'git update-server-info': %v", err)
|
||||
}
|
||||
|
||||
@@ -322,11 +299,12 @@ func runHookPostReceive(c *cli.Context) error {
|
||||
|
||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
||||
return fail(`Rejecting changes as Gitea environment not set.
|
||||
fail(`Rejecting changes as Gitea environment not set.
|
||||
If you are pushing over SSH you must push with a key managed by
|
||||
Gitea or set your environment appropriately.`, "")
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var out io.Writer
|
||||
@@ -342,9 +320,9 @@ Gitea or set your environment appropriately.`, "")
|
||||
}
|
||||
}
|
||||
|
||||
// the environment is set by serv command
|
||||
// the environment setted on serv command
|
||||
repoUser := os.Getenv(models.EnvRepoUsername)
|
||||
isWiki := os.Getenv(models.EnvRepoIsWiki) == "true"
|
||||
isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
|
||||
repoName := os.Getenv(models.EnvRepoName)
|
||||
pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||
pusherName := os.Getenv(models.EnvPusherName)
|
||||
@@ -393,11 +371,11 @@ Gitea or set your environment appropriately.`, "")
|
||||
hookOptions.OldCommitIDs = oldCommitIDs
|
||||
hookOptions.NewCommitIDs = newCommitIDs
|
||||
hookOptions.RefFullNames = refFullNames
|
||||
resp, err := private.HookPostReceive(ctx, repoUser, repoName, hookOptions)
|
||||
resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
|
||||
if resp == nil {
|
||||
_ = dWriter.Close()
|
||||
hookPrintResults(results)
|
||||
return fail("Internal Server Error", err)
|
||||
fail("Internal Server Error", err)
|
||||
}
|
||||
wasEmpty = wasEmpty || resp.RepoWasEmpty
|
||||
results = append(results, resp.Results...)
|
||||
@@ -408,9 +386,9 @@ Gitea or set your environment appropriately.`, "")
|
||||
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")
|
||||
err := private.SetDefaultBranch(repoUser, repoName, "master")
|
||||
if err != nil {
|
||||
return fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
|
||||
fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(out, "Processed %d references in total\n", total)
|
||||
@@ -426,11 +404,11 @@ Gitea or set your environment appropriately.`, "")
|
||||
|
||||
fmt.Fprintf(out, " Processing %d references\n", count)
|
||||
|
||||
resp, err := private.HookPostReceive(ctx, repoUser, repoName, hookOptions)
|
||||
resp, err := private.HookPostReceive(repoUser, repoName, hookOptions)
|
||||
if resp == nil {
|
||||
_ = dWriter.Close()
|
||||
hookPrintResults(results)
|
||||
return fail("Internal Server Error", err)
|
||||
fail("Internal Server Error", err)
|
||||
}
|
||||
wasEmpty = wasEmpty || resp.RepoWasEmpty
|
||||
results = append(results, resp.Results...)
|
||||
@@ -439,9 +417,9 @@ Gitea or set your environment appropriately.`, "")
|
||||
|
||||
if wasEmpty && masterPushed {
|
||||
// We need to tell the repo to reset the default branch to master
|
||||
err := private.SetDefaultBranch(ctx, repoUser, repoName, "master")
|
||||
err := private.SetDefaultBranch(repoUser, repoName, "master")
|
||||
if err != nil {
|
||||
return fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
|
||||
fail("Internal Server Error", "SetDefaultBranch failed with Error: %v", err)
|
||||
}
|
||||
}
|
||||
_ = dWriter.Close()
|
||||
@@ -482,327 +460,3 @@ func pushOptions() map[string]string {
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
func runHookProcReceive(c *cli.Context) error {
|
||||
setup("hooks/proc-receive.log", c.Bool("debug"))
|
||||
|
||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
||||
return fail(`Rejecting changes as Gitea environment not set.
|
||||
If you are pushing over SSH you must push with a key managed by
|
||||
Gitea or set your environment appropriately.`, "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if git.CheckGitVersionAtLeast("2.29") != nil {
|
||||
return fail("Internal Server Error", "git not support proc-receive.")
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
repoUser := os.Getenv(models.EnvRepoUsername)
|
||||
repoName := os.Getenv(models.EnvRepoName)
|
||||
pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||
pusherName := os.Getenv(models.EnvPusherName)
|
||||
|
||||
// 1. Version and features negotiation.
|
||||
// S: PKT-LINE(version=1\0push-options atomic...) / PKT-LINE(version=1\n)
|
||||
// S: flush-pkt
|
||||
// H: PKT-LINE(version=1\0push-options...)
|
||||
// H: flush-pkt
|
||||
|
||||
rs, err := readPktLine(reader, pktLineTypeData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
const VersionHead string = "version=1"
|
||||
|
||||
var (
|
||||
hasPushOptions bool
|
||||
response = []byte(VersionHead)
|
||||
requestOptions []string
|
||||
)
|
||||
|
||||
index := bytes.IndexByte(rs.Data, byte(0))
|
||||
if index >= len(rs.Data) {
|
||||
return fail("Internal Server Error", "pkt-line: format error "+fmt.Sprint(rs.Data))
|
||||
}
|
||||
|
||||
if index < 0 {
|
||||
if len(rs.Data) == 10 && rs.Data[9] == '\n' {
|
||||
index = 9
|
||||
} else {
|
||||
return fail("Internal Server Error", "pkt-line: format error "+fmt.Sprint(rs.Data))
|
||||
}
|
||||
}
|
||||
|
||||
if string(rs.Data[0:index]) != VersionHead {
|
||||
return fail("Internal Server Error", "Received unsupported version: %s", string(rs.Data[0:index]))
|
||||
}
|
||||
requestOptions = strings.Split(string(rs.Data[index+1:]), " ")
|
||||
|
||||
for _, option := range requestOptions {
|
||||
if strings.HasPrefix(option, "push-options") {
|
||||
response = append(response, byte(0))
|
||||
response = append(response, []byte("push-options")...)
|
||||
hasPushOptions = true
|
||||
}
|
||||
}
|
||||
response = append(response, '\n')
|
||||
|
||||
_, err = readPktLine(reader, pktLineTypeFlush)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = writeDataPktLine(os.Stdout, response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = writeFlushPktLine(os.Stdout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. receive commands from server.
|
||||
// S: PKT-LINE(<old-oid> <new-oid> <ref>)
|
||||
// S: ... ...
|
||||
// S: flush-pkt
|
||||
// # [receive push-options]
|
||||
// S: PKT-LINE(push-option)
|
||||
// S: ... ...
|
||||
// S: flush-pkt
|
||||
hookOptions := private.HookOptions{
|
||||
UserName: pusherName,
|
||||
UserID: pusherID,
|
||||
}
|
||||
hookOptions.OldCommitIDs = make([]string, 0, hookBatchSize)
|
||||
hookOptions.NewCommitIDs = make([]string, 0, hookBatchSize)
|
||||
hookOptions.RefFullNames = make([]string, 0, hookBatchSize)
|
||||
|
||||
for {
|
||||
// note: pktLineTypeUnknow means pktLineTypeFlush and pktLineTypeData all allowed
|
||||
rs, err = readPktLine(reader, pktLineTypeUnknow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rs.Type == pktLineTypeFlush {
|
||||
break
|
||||
}
|
||||
t := strings.SplitN(string(rs.Data), " ", 3)
|
||||
if len(t) != 3 {
|
||||
continue
|
||||
}
|
||||
hookOptions.OldCommitIDs = append(hookOptions.OldCommitIDs, t[0])
|
||||
hookOptions.NewCommitIDs = append(hookOptions.NewCommitIDs, t[1])
|
||||
hookOptions.RefFullNames = append(hookOptions.RefFullNames, t[2])
|
||||
}
|
||||
|
||||
hookOptions.GitPushOptions = make(map[string]string)
|
||||
|
||||
if hasPushOptions {
|
||||
for {
|
||||
rs, err = readPktLine(reader, pktLineTypeUnknow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rs.Type == pktLineTypeFlush {
|
||||
break
|
||||
}
|
||||
|
||||
kv := strings.SplitN(string(rs.Data), "=", 2)
|
||||
if len(kv) == 2 {
|
||||
hookOptions.GitPushOptions[kv[0]] = kv[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. run hook
|
||||
resp, err := private.HookProcReceive(ctx, repoUser, repoName, hookOptions)
|
||||
if err != nil {
|
||||
return fail("Internal Server Error", "run proc-receive hook failed :%v", err)
|
||||
}
|
||||
|
||||
// 4. response result to service
|
||||
// # a. OK, but has an alternate reference. The alternate reference name
|
||||
// # and other status can be given in option directives.
|
||||
// H: PKT-LINE(ok <ref>)
|
||||
// H: PKT-LINE(option refname <refname>)
|
||||
// H: PKT-LINE(option old-oid <old-oid>)
|
||||
// H: PKT-LINE(option new-oid <new-oid>)
|
||||
// H: PKT-LINE(option forced-update)
|
||||
// H: ... ...
|
||||
// H: flush-pkt
|
||||
// # b. NO, I reject it.
|
||||
// H: PKT-LINE(ng <ref> <reason>)
|
||||
// # c. Fall through, let 'receive-pack' to execute it.
|
||||
// H: PKT-LINE(ok <ref>)
|
||||
// H: PKT-LINE(option fall-through)
|
||||
|
||||
for _, rs := range resp.Results {
|
||||
if len(rs.Err) > 0 {
|
||||
err = writeDataPktLine(os.Stdout, []byte("ng "+rs.OriginalRef+" "+rs.Err))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if rs.IsNotMatched {
|
||||
err = writeDataPktLine(os.Stdout, []byte("ok "+rs.OriginalRef))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = writeDataPktLine(os.Stdout, []byte("option fall-through"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
err = writeDataPktLine(os.Stdout, []byte("ok "+rs.OriginalRef))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = writeDataPktLine(os.Stdout, []byte("option refname "+rs.Ref))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rs.OldOID != git.EmptySHA {
|
||||
err = writeDataPktLine(os.Stdout, []byte("option old-oid "+rs.OldOID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = writeDataPktLine(os.Stdout, []byte("option new-oid "+rs.NewOID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rs.IsForcePush {
|
||||
err = writeDataPktLine(os.Stdout, []byte("option forced-update"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
err = writeFlushPktLine(os.Stdout)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// git PKT-Line api
|
||||
// pktLineType message type of pkt-line
|
||||
type pktLineType int64
|
||||
|
||||
const (
|
||||
// UnKnow type
|
||||
pktLineTypeUnknow pktLineType = 0
|
||||
// flush-pkt "0000"
|
||||
pktLineTypeFlush pktLineType = iota
|
||||
// data line
|
||||
pktLineTypeData
|
||||
)
|
||||
|
||||
// gitPktLine pkt-line api
|
||||
type gitPktLine struct {
|
||||
Type pktLineType
|
||||
Length uint64
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func readPktLine(in *bufio.Reader, requestType pktLineType) (*gitPktLine, error) {
|
||||
var (
|
||||
err error
|
||||
r *gitPktLine
|
||||
)
|
||||
|
||||
// read prefix
|
||||
lengthBytes := make([]byte, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
lengthBytes[i], err = in.ReadByte()
|
||||
if err != nil {
|
||||
return nil, fail("Internal Server Error", "Pkt-Line: read stdin failed : %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
r = new(gitPktLine)
|
||||
r.Length, err = strconv.ParseUint(string(lengthBytes), 16, 32)
|
||||
if err != nil {
|
||||
return nil, fail("Internal Server Error", "Pkt-Line format is wrong :%v", err)
|
||||
}
|
||||
|
||||
if r.Length == 0 {
|
||||
if requestType == pktLineTypeData {
|
||||
return nil, fail("Internal Server Error", "Pkt-Line format is wrong")
|
||||
}
|
||||
r.Type = pktLineTypeFlush
|
||||
return r, nil
|
||||
}
|
||||
|
||||
if r.Length <= 4 || r.Length > 65520 || requestType == pktLineTypeFlush {
|
||||
return nil, fail("Internal Server Error", "Pkt-Line format is wrong")
|
||||
}
|
||||
|
||||
r.Data = make([]byte, r.Length-4)
|
||||
for i := range r.Data {
|
||||
r.Data[i], err = in.ReadByte()
|
||||
if err != nil {
|
||||
return nil, fail("Internal Server Error", "Pkt-Line: read stdin failed : %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
r.Type = pktLineTypeData
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func writeFlushPktLine(out io.Writer) error {
|
||||
l, err := out.Write([]byte("0000"))
|
||||
if err != nil {
|
||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
||||
}
|
||||
if l != 4 {
|
||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeDataPktLine(out io.Writer, data []byte) error {
|
||||
hexchar := []byte("0123456789abcdef")
|
||||
hex := func(n uint64) byte {
|
||||
return hexchar[(n)&15]
|
||||
}
|
||||
|
||||
length := uint64(len(data) + 4)
|
||||
tmp := make([]byte, 4)
|
||||
tmp[0] = hex(length >> 12)
|
||||
tmp[1] = hex(length >> 8)
|
||||
tmp[2] = hex(length >> 4)
|
||||
tmp[3] = hex(length)
|
||||
|
||||
lr, err := out.Write(tmp)
|
||||
if err != nil {
|
||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
||||
}
|
||||
if 4 != lr {
|
||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
||||
}
|
||||
|
||||
lr, err = out.Write(data)
|
||||
if err != nil {
|
||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
||||
}
|
||||
if int(length-4) != lr {
|
||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -1,41 +0,0 @@
|
||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPktLine(t *testing.T) {
|
||||
// test read
|
||||
s := strings.NewReader("0000")
|
||||
r := bufio.NewReader(s)
|
||||
result, err := readPktLine(r, pktLineTypeFlush)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, pktLineTypeFlush, result.Type)
|
||||
|
||||
s = strings.NewReader("0006a\n")
|
||||
r = bufio.NewReader(s)
|
||||
result, err = readPktLine(r, pktLineTypeData)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, pktLineTypeData, result.Type)
|
||||
assert.Equal(t, []byte("a\n"), result.Data)
|
||||
|
||||
// test write
|
||||
w := bytes.NewBuffer([]byte{})
|
||||
err = writeFlushPktLine(w)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []byte("0000"), w.Bytes())
|
||||
|
||||
w.Reset()
|
||||
err = writeDataPktLine(w, []byte("a\nb"))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []byte("0007a\nb"), w.Bytes())
|
||||
}
|
@@ -62,12 +62,9 @@ func runKeys(c *cli.Context) error {
|
||||
return errors.New("No key type and content provided")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setup("keys.log", false)
|
||||
|
||||
authorizedString, err := private.AuthorizedPublicKeyByContent(ctx, content)
|
||||
authorizedString, err := private.AuthorizedPublicKeyByContent(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -1,55 +0,0 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func runSendMail(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setting.LoadFromExisting()
|
||||
|
||||
if err := argsSet(c, "title"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
subject := c.String("title")
|
||||
confirmSkiped := c.Bool("force")
|
||||
body := c.String("content")
|
||||
|
||||
if !confirmSkiped {
|
||||
if len(body) == 0 {
|
||||
fmt.Print("warning: Content is empty")
|
||||
}
|
||||
|
||||
fmt.Print("Proceed with sending email? [Y/n] ")
|
||||
isConfirmed, err := confirm()
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !isConfirmed {
|
||||
fmt.Println("The mail was not sent")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
status, message := private.SendEmail(ctx, subject, body, nil)
|
||||
if status != http.StatusOK {
|
||||
fmt.Printf("error: %s\n", message)
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("Success: %s\n", message)
|
||||
|
||||
return nil
|
||||
}
|
@@ -236,13 +236,10 @@ func runRemoveLogger(c *cli.Context) error {
|
||||
group = log.DEFAULT
|
||||
}
|
||||
name := c.Args().First()
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
statusCode, msg := private.RemoveLogger(ctx, group, name)
|
||||
statusCode, msg := private.RemoveLogger(group, name)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
@@ -374,13 +371,10 @@ func commonAddLogger(c *cli.Context, mode string, vals map[string]interface{}) e
|
||||
if c.IsSet("name") {
|
||||
name = c.String("name")
|
||||
}
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
statusCode, msg := private.AddLogger(ctx, group, name, mode, vals)
|
||||
statusCode, msg := private.AddLogger(group, name, mode, vals)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
@@ -388,14 +382,11 @@ func commonAddLogger(c *cli.Context, mode string, vals map[string]interface{}) e
|
||||
}
|
||||
|
||||
func runShutdown(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.Shutdown(ctx)
|
||||
statusCode, msg := private.Shutdown()
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
@@ -403,14 +394,11 @@ func runShutdown(c *cli.Context) error {
|
||||
}
|
||||
|
||||
func runRestart(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.Restart(ctx)
|
||||
statusCode, msg := private.Restart()
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
@@ -418,14 +406,11 @@ func runRestart(c *cli.Context) error {
|
||||
}
|
||||
|
||||
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"))
|
||||
statusCode, msg := private.FlushQueues(c.Duration("timeout"), c.Bool("non-blocking"))
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
@@ -433,14 +418,11 @@ func runFlushQueues(c *cli.Context) error {
|
||||
}
|
||||
|
||||
func runPauseLogging(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.PauseLogging(ctx)
|
||||
statusCode, msg := private.PauseLogging()
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
@@ -448,14 +430,11 @@ func runPauseLogging(c *cli.Context) error {
|
||||
}
|
||||
|
||||
func runResumeLogging(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.ResumeLogging(ctx)
|
||||
statusCode, msg := private.ResumeLogging()
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
@@ -463,14 +442,11 @@ func runResumeLogging(c *cli.Context) error {
|
||||
}
|
||||
|
||||
func runReleaseReopenLogging(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.ReleaseReopenLogging(ctx)
|
||||
statusCode, msg := private.ReleaseReopenLogging()
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
|
@@ -7,7 +7,7 @@ package cmd
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/migrations"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@@ -24,20 +24,17 @@ var CmdMigrate = cli.Command{
|
||||
}
|
||||
|
||||
func runMigrate(ctx *cli.Context) error {
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(stdCtx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("AppPath: %s", setting.AppPath)
|
||||
log.Info("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Info("Custom path: %s", setting.CustomPath)
|
||||
log.Info("Log path: %s", setting.LogRootPath)
|
||||
log.Info("Configuration file: %s", setting.CustomConf)
|
||||
log.Trace("AppPath: %s", setting.AppPath)
|
||||
log.Trace("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Trace("Custom path: %s", setting.CustomPath)
|
||||
log.Trace("Log path: %s", setting.LogRootPath)
|
||||
setting.InitDBConfig()
|
||||
|
||||
if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
|
||||
if err := models.NewEngine(context.Background(), migrations.Migrate); err != nil {
|
||||
log.Fatal("Failed to initialize ORM engine: %v", err)
|
||||
return err
|
||||
}
|
||||
|
@@ -10,10 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/migrations"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
@@ -81,7 +78,7 @@ var CmdMigrateStorage = cli.Command{
|
||||
}
|
||||
|
||||
func migrateAttachments(dstStorage storage.ObjectStorage) error {
|
||||
return repo_model.IterateAttachment(func(attach *repo_model.Attachment) error {
|
||||
return models.IterateAttachment(func(attach *models.Attachment) error {
|
||||
_, err := storage.Copy(dstStorage, attach.RelativePath(), storage.Attachments, attach.RelativePath())
|
||||
return err
|
||||
})
|
||||
@@ -95,34 +92,31 @@ func migrateLFS(dstStorage storage.ObjectStorage) error {
|
||||
}
|
||||
|
||||
func migrateAvatars(dstStorage storage.ObjectStorage) error {
|
||||
return user_model.IterateUser(func(user *user_model.User) 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 repo_model.IterateRepository(func(repo *repo_model.Repository) 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 {
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(stdCtx); err != nil {
|
||||
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)
|
||||
log.Info("Configuration file: %s", setting.CustomConf)
|
||||
log.Trace("AppPath: %s", setting.AppPath)
|
||||
log.Trace("AppWorkPath: %s", setting.AppWorkPath)
|
||||
log.Trace("Custom path: %s", setting.CustomPath)
|
||||
log.Trace("Log path: %s", setting.LogRootPath)
|
||||
setting.InitDBConfig()
|
||||
|
||||
if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
|
||||
if err := models.NewEngine(context.Background(), migrations.Migrate); err != nil {
|
||||
log.Fatal("Failed to initialize ORM engine: %v", err)
|
||||
return err
|
||||
}
|
||||
@@ -190,7 +184,7 @@ func runMigrateStorage(ctx *cli.Context) error {
|
||||
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.")
|
||||
log.Warn("All files have been copied to the new placement but old files are still on the orignial placement.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -1,68 +0,0 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// CmdRestoreRepository represents the available restore a repository sub-command.
|
||||
var CmdRestoreRepository = cli.Command{
|
||||
Name: "restore-repo",
|
||||
Usage: "Restore the repository from disk",
|
||||
Description: "This is a command for restoring the repository data.",
|
||||
Action: runRestoreRepository,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "repo_dir, r",
|
||||
Value: "./data",
|
||||
Usage: "Repository dir path to restore from",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "owner_name",
|
||||
Value: "",
|
||||
Usage: "Restore destination owner name",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "repo_name",
|
||||
Value: "",
|
||||
Usage: "Restore destination repository name",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "units",
|
||||
Value: "",
|
||||
Usage: `Which items will be restored, one or more units should be separated as comma.
|
||||
wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func runRestoreRepository(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setting.LoadFromExisting()
|
||||
|
||||
statusCode, errStr := private.RestoreRepo(
|
||||
ctx,
|
||||
c.String("repo_dir"),
|
||||
c.String("owner_name"),
|
||||
c.String("repo_name"),
|
||||
c.StringSlice("units"),
|
||||
)
|
||||
if statusCode == http.StatusOK {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Fatal("Failed to restore repository: %v", errStr)
|
||||
return errors.New(errStr)
|
||||
}
|
115
cmd/serv.go
115
cmd/serv.go
@@ -6,6 +6,7 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -17,18 +18,15 @@ import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/lfs"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/pprof"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/services/lfs"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/kballard/go-shellquote"
|
||||
"github.com/unknwon/com"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
@@ -59,46 +57,35 @@ func setup(logPath string, debug bool) {
|
||||
} else {
|
||||
_ = log.NewLogger(1000, "console", "console", `{"level":"fatal","stacktracelevel":"NONE","stderr":true}`)
|
||||
}
|
||||
setting.LoadFromExisting()
|
||||
setting.NewContext()
|
||||
if debug {
|
||||
setting.RunMode = "dev"
|
||||
setting.ProdMode = false
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
allowedCommands = map[string]perm.AccessMode{
|
||||
"git-upload-pack": perm.AccessModeRead,
|
||||
"git-upload-archive": perm.AccessModeRead,
|
||||
"git-receive-pack": perm.AccessModeWrite,
|
||||
lfsAuthenticateVerb: perm.AccessModeNone,
|
||||
allowedCommands = map[string]models.AccessMode{
|
||||
"git-upload-pack": models.AccessModeRead,
|
||||
"git-upload-archive": models.AccessModeRead,
|
||||
"git-receive-pack": models.AccessModeWrite,
|
||||
lfsAuthenticateVerb: models.AccessModeNone,
|
||||
}
|
||||
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||
)
|
||||
|
||||
func fail(userMessage, logMessage string, args ...interface{}) error {
|
||||
// There appears to be a chance to cause a zombie process and failure to read the Exit status
|
||||
// if nothing is outputted on stdout.
|
||||
fmt.Fprintln(os.Stdout, "")
|
||||
func fail(userMessage, logMessage string, args ...interface{}) {
|
||||
fmt.Fprintln(os.Stderr, "Gitea:", userMessage)
|
||||
|
||||
if len(logMessage) > 0 {
|
||||
if !setting.IsProd {
|
||||
if !setting.ProdMode {
|
||||
fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
|
||||
}
|
||||
}
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if len(logMessage) > 0 {
|
||||
_ = private.SSHLog(ctx, true, fmt.Sprintf(logMessage+": ", args...))
|
||||
}
|
||||
return cli.NewExitError("", 1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func runServ(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
// FIXME: This needs to internationalised
|
||||
setup("serv.log", c.Bool("debug"))
|
||||
|
||||
@@ -116,23 +103,20 @@ func runServ(c *cli.Context) error {
|
||||
|
||||
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])
|
||||
fail("Key ID format error", "Invalid key argument: %s", c.Args()[0])
|
||||
}
|
||||
keyID := com.StrTo(keys[1]).MustInt64()
|
||||
|
||||
cmd := os.Getenv("SSH_ORIGINAL_COMMAND")
|
||||
if len(cmd) == 0 {
|
||||
key, user, err := private.ServNoCommand(ctx, keyID)
|
||||
key, user, err := private.ServNoCommand(keyID)
|
||||
if err != nil {
|
||||
return fail("Internal error", "Failed to check provided key: %v", err)
|
||||
fail("Internal error", "Failed to check provided key: %v", err)
|
||||
}
|
||||
switch key.Type {
|
||||
case asymkey_model.KeyTypeDeploy:
|
||||
case models.KeyTypeDeploy:
|
||||
println("Hi there! You've successfully authenticated with the deploy key named " + key.Name + ", but Gitea does not provide shell access.")
|
||||
case asymkey_model.KeyTypePrincipal:
|
||||
case models.KeyTypePrincipal:
|
||||
println("Hi there! You've successfully authenticated with the principal " + key.Content + ", but Gitea does not provide shell access.")
|
||||
default:
|
||||
println("Hi there, " + user.Name + "! You've successfully authenticated with the key named " + key.Name + ", but Gitea does not provide shell access.")
|
||||
@@ -145,18 +129,11 @@ func runServ(c *cli.Context) error {
|
||||
|
||||
words, err := shellquote.Split(cmd)
|
||||
if err != nil {
|
||||
return fail("Error parsing arguments", "Failed to parse arguments: %v", err)
|
||||
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)
|
||||
fail("Too few arguments", "Too few arguments in cmd: %s", cmd)
|
||||
}
|
||||
|
||||
verb := words[0]
|
||||
@@ -168,7 +145,7 @@ func runServ(c *cli.Context) error {
|
||||
var lfsVerb string
|
||||
if verb == lfsAuthenticateVerb {
|
||||
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 {
|
||||
@@ -181,59 +158,60 @@ func runServ(c *cli.Context) error {
|
||||
|
||||
rr := strings.SplitN(repoPath, "/", 2)
|
||||
if len(rr) != 2 {
|
||||
return fail("Invalid repository path", "Invalid repository path: %v", repoPath)
|
||||
fail("Invalid repository path", "Invalid repository path: %v", repoPath)
|
||||
}
|
||||
|
||||
username := strings.ToLower(rr[0])
|
||||
reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
|
||||
|
||||
if alphaDashDotPattern.MatchString(reponame) {
|
||||
return fail("Invalid repo name", "Invalid repo name: %s", reponame)
|
||||
fail("Invalid repo name", "Invalid repo name: %s", reponame)
|
||||
}
|
||||
|
||||
if c.Bool("enable-pprof") {
|
||||
if setting.EnablePprof || c.Bool("enable-pprof") {
|
||||
if err := os.MkdirAll(setting.PprofDataPath, os.ModePerm); err != nil {
|
||||
return fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err)
|
||||
fail("Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err)
|
||||
}
|
||||
|
||||
stopCPUProfiler, err := pprof.DumpCPUProfileForUsername(setting.PprofDataPath, username)
|
||||
if err != nil {
|
||||
return fail("Internal Server Error", "Unable to start CPU profile: %v", err)
|
||||
fail("Internal Server Error", "Unable to start CPU profile: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
stopCPUProfiler()
|
||||
err := pprof.DumpMemProfileForUsername(setting.PprofDataPath, username)
|
||||
if err != nil {
|
||||
_ = fail("Internal Server Error", "Unable to dump Mem Profile: %v", err)
|
||||
fail("Internal Server Error", "Unable to dump Mem Profile: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
requestedMode, has := allowedCommands[verb]
|
||||
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 lfsVerb == "upload" {
|
||||
requestedMode = perm.AccessModeWrite
|
||||
requestedMode = models.AccessModeWrite
|
||||
} else if lfsVerb == "download" {
|
||||
requestedMode = perm.AccessModeRead
|
||||
requestedMode = models.AccessModeRead
|
||||
} else {
|
||||
return fail("Unknown LFS verb", "Unknown lfs verb %s", lfsVerb)
|
||||
fail("Unknown LFS verb", "Unknown lfs verb %s", lfsVerb)
|
||||
}
|
||||
}
|
||||
|
||||
results, err := private.ServCommand(ctx, keyID, username, reponame, requestedMode, verb, lfsVerb)
|
||||
results, err := private.ServCommand(keyID, username, reponame, requestedMode, verb, lfsVerb)
|
||||
if err != nil {
|
||||
if private.IsErrServCommand(err) {
|
||||
errServCommand := err.(private.ErrServCommand)
|
||||
if errServCommand.StatusCode != http.StatusInternalServerError {
|
||||
return fail("Unauthorized", "%s", errServCommand.Error())
|
||||
fail("Unauthorized", "%s", errServCommand.Error())
|
||||
} else {
|
||||
fail("Internal Server Error", "%s", errServCommand.Error())
|
||||
}
|
||||
return fail("Internal Server Error", "%s", errServCommand.Error())
|
||||
}
|
||||
return fail("Internal Server Error", "%s", err.Error())
|
||||
fail("Internal Server Error", "%s", err.Error())
|
||||
}
|
||||
os.Setenv(models.EnvRepoIsWiki, strconv.FormatBool(results.IsWiki))
|
||||
os.Setenv(models.EnvRepoName, results.RepoName)
|
||||
@@ -253,8 +231,7 @@ func runServ(c *cli.Context) error {
|
||||
|
||||
now := time.Now()
|
||||
claims := lfs.Claims{
|
||||
// FIXME: we need to migrate to RegisteredClaims
|
||||
StandardClaims: jwt.StandardClaims{ // nolint
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
ExpiresAt: now.Add(setting.LFS.HTTPAuthExpiry).Unix(),
|
||||
NotBefore: now.Unix(),
|
||||
},
|
||||
@@ -267,7 +244,7 @@ func runServ(c *cli.Context) error {
|
||||
// Sign and get the complete encoded token as a string using the secret
|
||||
tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes)
|
||||
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{
|
||||
@@ -279,7 +256,7 @@ func runServ(c *cli.Context) error {
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
err = enc.Encode(tokenAuthentication)
|
||||
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
|
||||
}
|
||||
@@ -292,9 +269,9 @@ func runServ(c *cli.Context) error {
|
||||
var gitcmd *exec.Cmd
|
||||
verbs := strings.Split(verb, " ")
|
||||
if len(verbs) == 2 {
|
||||
gitcmd = exec.CommandContext(ctx, verbs[0], verbs[1], repoPath)
|
||||
gitcmd = exec.Command(verbs[0], verbs[1], repoPath)
|
||||
} else {
|
||||
gitcmd = exec.CommandContext(ctx, verb, repoPath)
|
||||
gitcmd = exec.Command(verb, repoPath)
|
||||
}
|
||||
|
||||
gitcmd.Dir = setting.RepoRootPath
|
||||
@@ -302,13 +279,13 @@ func runServ(c *cli.Context) error {
|
||||
gitcmd.Stdin = os.Stdin
|
||||
gitcmd.Stderr = os.Stderr
|
||||
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.
|
||||
if results.KeyID > 0 {
|
||||
if err = private.UpdatePublicKeyInRepo(ctx, results.KeyID, results.RepoID); err != nil {
|
||||
return fail("Internal error", "UpdatePublicKeyInRepo: %v", err)
|
||||
if err = private.UpdatePublicKeyInRepo(results.KeyID, results.RepoID); err != nil {
|
||||
fail("Internal error", "UpdatePublicKeyInRepo: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
249
cmd/web.go
249
cmd/web.go
@@ -9,18 +9,20 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
|
||||
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/routers"
|
||||
"code.gitea.io/gitea/routers/install"
|
||||
"code.gitea.io/gitea/routers/routes"
|
||||
|
||||
context2 "github.com/gorilla/context"
|
||||
"github.com/unknwon/com"
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
@@ -37,24 +39,11 @@ and it takes care of all the other things for you`,
|
||||
Value: "3000",
|
||||
Usage: "Temporary port number to prevent conflict",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "install-port",
|
||||
Value: "3000",
|
||||
Usage: "Temporary port number to run the install page on to prevent conflict",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "pid, P",
|
||||
Value: setting.PIDFile,
|
||||
Usage: "Custom pid file path",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "quiet, q",
|
||||
Usage: "Only display Fatal logging errors until logging is set-up",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Usage: "Set initial logging to TRACE level until logging is properly set-up",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -71,27 +60,44 @@ func runHTTPRedirector() {
|
||||
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
||||
})
|
||||
|
||||
var err = runHTTP("tcp", source, "HTTP Redirector", handler)
|
||||
var err = runHTTP("tcp", source, context2.ClearHandler(handler))
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("Failed to start port redirection: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func runWeb(ctx *cli.Context) error {
|
||||
if ctx.Bool("verbose") {
|
||||
_ = log.DelLogger("console")
|
||||
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))
|
||||
func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler) error {
|
||||
certManager := autocert.Manager{
|
||||
Prompt: autocert.AcceptTOS,
|
||||
HostPolicy: autocert.HostWhitelist(domain),
|
||||
Cache: autocert.DirCache(directory),
|
||||
Email: email,
|
||||
}
|
||||
defer func() {
|
||||
if panicked := recover(); panicked != nil {
|
||||
log.Fatal("PANIC: %v\n%s", panicked, string(log.Stack(2)))
|
||||
go func() {
|
||||
log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
|
||||
// all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
|
||||
var err = runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, certManager.HTTPHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)))
|
||||
if err != nil {
|
||||
log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
|
||||
}
|
||||
}()
|
||||
return runHTTPSWithTLSConfig("tcp", listenAddr, certManager.TLSConfig(), context2.ClearHandler(m))
|
||||
}
|
||||
|
||||
func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "GET" && r.Method != "HEAD" {
|
||||
http.Error(w, "Use HTTPS", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// Remove the trailing slash at the end of setting.AppURL, the request
|
||||
// URI always contains a leading slash, which would result in a double
|
||||
// slash
|
||||
target := strings.TrimSuffix(setting.AppURL, "/") + r.URL.RequestURI()
|
||||
http.Redirect(w, r, target, http.StatusFound)
|
||||
}
|
||||
|
||||
func runWeb(ctx *cli.Context) error {
|
||||
managerCtx, cancel := context.WithCancel(context.Background())
|
||||
graceful.InitManager(managerCtx)
|
||||
defer cancel()
|
||||
@@ -108,36 +114,56 @@ func runWeb(ctx *cli.Context) error {
|
||||
setting.WritePIDFile = true
|
||||
}
|
||||
|
||||
// Perform pre-initialization
|
||||
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)
|
||||
if err != nil {
|
||||
log.Critical("Unable to open listener for installer. Is Gitea already running?")
|
||||
graceful.GetManager().DoGracefulShutdown()
|
||||
}
|
||||
select {
|
||||
case <-graceful.GetManager().IsShutdown():
|
||||
<-graceful.GetManager().Done()
|
||||
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
||||
log.Close()
|
||||
return err
|
||||
// Perform global initialization
|
||||
routers.GlobalInit(graceful.GetManager().HammerContext())
|
||||
|
||||
// Set up Macaron
|
||||
m := routes.NewMacaron()
|
||||
routes.RegisterRoutes(m)
|
||||
|
||||
// Flag for port number in case first time run conflict.
|
||||
if ctx.IsSet("port") {
|
||||
setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, ctx.String("port"), 1)
|
||||
setting.HTTPPort = ctx.String("port")
|
||||
|
||||
switch setting.Protocol {
|
||||
case setting.UnixSocket:
|
||||
case setting.FCGI:
|
||||
case setting.FCGIUnix:
|
||||
default:
|
||||
// Save LOCAL_ROOT_URL if port changed
|
||||
cfg := ini.Empty()
|
||||
if com.IsFile(setting.CustomConf) {
|
||||
// Keeps custom settings if there is already something.
|
||||
if err := cfg.Append(setting.CustomConf); err != nil {
|
||||
return fmt.Errorf("Failed to load custom conf '%s': %v", setting.CustomConf, err)
|
||||
}
|
||||
}
|
||||
|
||||
defaultLocalURL := string(setting.Protocol) + "://"
|
||||
if setting.HTTPAddr == "0.0.0.0" {
|
||||
defaultLocalURL += "localhost"
|
||||
} else {
|
||||
defaultLocalURL += setting.HTTPAddr
|
||||
}
|
||||
defaultLocalURL += ":" + setting.HTTPPort + "/"
|
||||
|
||||
cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
|
||||
|
||||
if err := cfg.SaveTo(setting.CustomConf); err != nil {
|
||||
return fmt.Errorf("Error saving generated JWT Secret to custom config: %v", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NoInstallListener()
|
||||
}
|
||||
|
||||
listenAddr := setting.HTTPAddr
|
||||
if setting.Protocol != setting.UnixSocket && setting.Protocol != setting.FCGIUnix {
|
||||
listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort)
|
||||
}
|
||||
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
|
||||
|
||||
if setting.LFS.StartServer {
|
||||
log.Info("LFS server enabled")
|
||||
}
|
||||
|
||||
if setting.EnablePprof {
|
||||
@@ -147,109 +173,31 @@ func runWeb(ctx *cli.Context) error {
|
||||
}()
|
||||
}
|
||||
|
||||
log.Info("Global init")
|
||||
// Perform global initialization
|
||||
setting.LoadFromExisting()
|
||||
routers.GlobalInitInstalled(graceful.GetManager().HammerContext())
|
||||
|
||||
// We check that AppDataPath exists here (it should have been created during installation)
|
||||
// We can't check it in `GlobalInitInstalled`, because some integration tests
|
||||
// use cmd -> GlobalInitInstalled, but the AppDataPath doesn't exist during those tests.
|
||||
if _, err := os.Stat(setting.AppDataPath); err != nil {
|
||||
log.Fatal("Can not find APP_DATA_PATH '%s'", setting.AppDataPath)
|
||||
}
|
||||
|
||||
// Override the provided port number within the configuration
|
||||
if ctx.IsSet("port") {
|
||||
if err := setPort(ctx.String("port")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Set up Chi routes
|
||||
c := routers.NormalRoutes()
|
||||
err := listen(c, true)
|
||||
<-graceful.GetManager().Done()
|
||||
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
||||
log.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
func setPort(port string) error {
|
||||
setting.AppURL = strings.Replace(setting.AppURL, setting.HTTPPort, port, 1)
|
||||
setting.HTTPPort = port
|
||||
|
||||
switch setting.Protocol {
|
||||
case setting.HTTPUnix:
|
||||
case setting.FCGI:
|
||||
case setting.FCGIUnix:
|
||||
default:
|
||||
defaultLocalURL := string(setting.Protocol) + "://"
|
||||
if setting.HTTPAddr == "0.0.0.0" {
|
||||
defaultLocalURL += "localhost"
|
||||
} else {
|
||||
defaultLocalURL += setting.HTTPAddr
|
||||
}
|
||||
defaultLocalURL += ":" + setting.HTTPPort + "/"
|
||||
|
||||
// Save LOCAL_ROOT_URL if port changed
|
||||
setting.CreateOrAppendToCustomConf(func(cfg *ini.File) {
|
||||
cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func listen(m http.Handler, handleRedirector bool) error {
|
||||
listenAddr := setting.HTTPAddr
|
||||
if setting.Protocol != setting.HTTPUnix && setting.Protocol != setting.FCGIUnix {
|
||||
listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort)
|
||||
}
|
||||
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
|
||||
// This can be useful for users, many users do wrong to their config and get strange behaviors behind a reverse-proxy.
|
||||
// A user may fix the configuration mistake when he sees this log.
|
||||
// And this is also very helpful to maintainers to provide help to users to resolve their configuration problems.
|
||||
log.Info("AppURL(ROOT_URL): %s", setting.AppURL)
|
||||
|
||||
if setting.LFS.StartServer {
|
||||
log.Info("LFS server enabled")
|
||||
}
|
||||
|
||||
var err error
|
||||
switch setting.Protocol {
|
||||
case setting.HTTP:
|
||||
if handleRedirector {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runHTTP("tcp", listenAddr, "Web", m)
|
||||
NoHTTPRedirector()
|
||||
err = runHTTP("tcp", listenAddr, context2.ClearHandler(m))
|
||||
case setting.HTTPS:
|
||||
if setting.EnableLetsEncrypt {
|
||||
err = runLetsEncrypt(listenAddr, setting.Domain, setting.LetsEncryptDirectory, setting.LetsEncryptEmail, m)
|
||||
err = runLetsEncrypt(listenAddr, setting.Domain, setting.LetsEncryptDirectory, setting.LetsEncryptEmail, context2.ClearHandler(m))
|
||||
break
|
||||
}
|
||||
if handleRedirector {
|
||||
if setting.RedirectOtherPort {
|
||||
go runHTTPRedirector()
|
||||
} else {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
if setting.RedirectOtherPort {
|
||||
go runHTTPRedirector()
|
||||
} else {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, m)
|
||||
err = runHTTPS("tcp", listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
|
||||
case setting.FCGI:
|
||||
if handleRedirector {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runFCGI("tcp", listenAddr, "FCGI Web", m)
|
||||
case setting.HTTPUnix:
|
||||
if handleRedirector {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runHTTP("unix", listenAddr, "Web", m)
|
||||
NoHTTPRedirector()
|
||||
err = runFCGI("tcp", listenAddr, context2.ClearHandler(m))
|
||||
case setting.UnixSocket:
|
||||
NoHTTPRedirector()
|
||||
err = runHTTP("unix", listenAddr, context2.ClearHandler(m))
|
||||
case setting.FCGIUnix:
|
||||
if handleRedirector {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runFCGI("unix", listenAddr, "Web", m)
|
||||
NoHTTPRedirector()
|
||||
err = runFCGI("unix", listenAddr, context2.ClearHandler(m))
|
||||
default:
|
||||
log.Fatal("Invalid protocol: %s", setting.Protocol)
|
||||
}
|
||||
@@ -258,5 +206,8 @@ func listen(m http.Handler, handleRedirector bool) error {
|
||||
log.Critical("Failed to start server: %v", err)
|
||||
}
|
||||
log.Info("HTTP Listener: %s Closed", listenAddr)
|
||||
return err
|
||||
<-graceful.GetManager().Done()
|
||||
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
||||
log.Close()
|
||||
return nil
|
||||
}
|
||||
|
@@ -5,18 +5,25 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/fcgi"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
func runHTTP(network, listenAddr, name string, m http.Handler) error {
|
||||
return graceful.HTTPListenAndServe(network, listenAddr, name, m)
|
||||
func runHTTP(network, listenAddr string, m http.Handler) error {
|
||||
return graceful.HTTPListenAndServe(network, listenAddr, m)
|
||||
}
|
||||
|
||||
func runHTTPS(network, listenAddr, certFile, keyFile string, m http.Handler) error {
|
||||
return graceful.HTTPListenAndServeTLS(network, listenAddr, certFile, keyFile, m)
|
||||
}
|
||||
|
||||
func runHTTPSWithTLSConfig(network, listenAddr string, tlsConfig *tls.Config, m http.Handler) error {
|
||||
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, tlsConfig, m)
|
||||
}
|
||||
|
||||
// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
|
||||
@@ -30,23 +37,12 @@ func NoMainListener() {
|
||||
graceful.GetManager().InformCleanup()
|
||||
}
|
||||
|
||||
// NoInstallListener tells our cleanup routine that we will not be using a possibly provided listener
|
||||
// for our install HTTP/HTTPS service
|
||||
func NoInstallListener() {
|
||||
graceful.GetManager().InformCleanup()
|
||||
}
|
||||
|
||||
func runFCGI(network, listenAddr, name string, m http.Handler) error {
|
||||
func runFCGI(network, listenAddr string, m http.Handler) error {
|
||||
// This needs to handle stdin as fcgi point
|
||||
fcgiServer := graceful.NewServer(network, listenAddr, name)
|
||||
fcgiServer := graceful.NewServer(network, listenAddr)
|
||||
|
||||
err := fcgiServer.ListenAndServe(func(listener net.Listener) error {
|
||||
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)
|
||||
}))
|
||||
return fcgi.Serve(listener, m)
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal("Failed to start FCGI main server: %v", err)
|
||||
|
192
cmd/web_https.go
192
cmd/web_https.go
@@ -1,192 +0,0 @@
|
||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/klauspost/cpuid/v2"
|
||||
)
|
||||
|
||||
var tlsVersionStringMap = map[string]uint16{
|
||||
"": tls.VersionTLS12, // Default to tls.VersionTLS12
|
||||
"tlsv1.0": tls.VersionTLS10,
|
||||
"tlsv1.1": tls.VersionTLS11,
|
||||
"tlsv1.2": tls.VersionTLS12,
|
||||
"tlsv1.3": tls.VersionTLS13,
|
||||
}
|
||||
|
||||
func toTLSVersion(version string) uint16 {
|
||||
tlsVersion, ok := tlsVersionStringMap[strings.TrimSpace(strings.ToLower(version))]
|
||||
if !ok {
|
||||
log.Warn("Unknown tls version: %s", version)
|
||||
return 0
|
||||
}
|
||||
return tlsVersion
|
||||
}
|
||||
|
||||
var curveStringMap = map[string]tls.CurveID{
|
||||
"x25519": tls.X25519,
|
||||
"p256": tls.CurveP256,
|
||||
"p384": tls.CurveP384,
|
||||
"p521": tls.CurveP521,
|
||||
}
|
||||
|
||||
func toCurvePreferences(preferences []string) []tls.CurveID {
|
||||
ids := make([]tls.CurveID, 0, len(preferences))
|
||||
for _, pref := range preferences {
|
||||
id, ok := curveStringMap[strings.TrimSpace(strings.ToLower(pref))]
|
||||
if !ok {
|
||||
log.Warn("Unknown curve: %s", pref)
|
||||
}
|
||||
if id != 0 {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
var cipherStringMap = map[string]uint16{
|
||||
"rsa_with_rc4_128_sha": tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||
"rsa_with_3des_ede_cbc_sha": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
"rsa_with_aes_128_cbc_sha": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
"rsa_with_aes_256_cbc_sha": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
"rsa_with_aes_128_cbc_sha256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
"rsa_with_aes_128_gcm_sha256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"rsa_with_aes_256_gcm_sha384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"ecdhe_ecdsa_with_rc4_128_sha": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
"ecdhe_ecdsa_with_aes_128_cbc_sha": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
"ecdhe_ecdsa_with_aes_256_cbc_sha": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
"ecdhe_rsa_with_rc4_128_sha": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
"ecdhe_rsa_with_3des_ede_cbc_sha": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
"ecdhe_rsa_with_aes_128_cbc_sha": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
"ecdhe_rsa_with_aes_256_cbc_sha": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
"ecdhe_ecdsa_with_aes_128_cbc_sha256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
"ecdhe_rsa_with_aes_128_cbc_sha256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
"ecdhe_rsa_with_aes_128_gcm_sha256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
"ecdhe_ecdsa_with_aes_128_gcm_sha256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
"ecdhe_rsa_with_aes_256_gcm_sha384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
"ecdhe_ecdsa_with_aes_256_gcm_sha384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
"ecdhe_rsa_with_chacha20_poly1305_sha256": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
"ecdhe_ecdsa_with_chacha20_poly1305_sha256": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
"ecdhe_rsa_with_chacha20_poly1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
"ecdhe_ecdsa_with_chacha20_poly1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
"aes_128_gcm_sha256": tls.TLS_AES_128_GCM_SHA256,
|
||||
"aes_256_gcm_sha384": tls.TLS_AES_256_GCM_SHA384,
|
||||
"chacha20_poly1305_sha256": tls.TLS_CHACHA20_POLY1305_SHA256,
|
||||
}
|
||||
|
||||
func toTLSCiphers(cipherStrings []string) []uint16 {
|
||||
ciphers := make([]uint16, 0, len(cipherStrings))
|
||||
for _, cipherString := range cipherStrings {
|
||||
cipher, ok := cipherStringMap[strings.TrimSpace(strings.ToLower(cipherString))]
|
||||
if !ok {
|
||||
log.Warn("Unknown cipher: %s", cipherString)
|
||||
}
|
||||
if cipher != 0 {
|
||||
ciphers = append(ciphers, cipher)
|
||||
}
|
||||
}
|
||||
|
||||
return ciphers
|
||||
}
|
||||
|
||||
// defaultCiphers uses hardware support to check if AES is specifically
|
||||
// supported by the CPU.
|
||||
//
|
||||
// If AES is supported AES ciphers will be preferred over ChaCha based ciphers
|
||||
// (This code is directly inspired by the certmagic code.)
|
||||
func defaultCiphers() []uint16 {
|
||||
if cpuid.CPU.Supports(cpuid.AESNI) {
|
||||
return defaultCiphersAESfirst
|
||||
}
|
||||
return defaultCiphersChaChaFirst
|
||||
}
|
||||
|
||||
var (
|
||||
defaultCiphersAES = []uint16{
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
}
|
||||
|
||||
defaultCiphersChaCha = []uint16{
|
||||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
}
|
||||
|
||||
defaultCiphersAESfirst = append(defaultCiphersAES, defaultCiphersChaCha...)
|
||||
defaultCiphersChaChaFirst = append(defaultCiphersChaCha, defaultCiphersAES...)
|
||||
)
|
||||
|
||||
// runHTTPs listens on the provided network address and then calls
|
||||
// Serve to handle requests on incoming TLS connections.
|
||||
//
|
||||
// Filenames containing a certificate and matching private key for the server must
|
||||
// be provided. If the certificate is signed by a certificate authority, the
|
||||
// certFile should be the concatenation of the server's certificate followed by the
|
||||
// CA's certificate.
|
||||
func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler) error {
|
||||
tlsConfig := &tls.Config{}
|
||||
if tlsConfig.NextProtos == nil {
|
||||
tlsConfig.NextProtos = []string{"h2", "http/1.1"}
|
||||
}
|
||||
|
||||
if version := toTLSVersion(setting.SSLMinimumVersion); version != 0 {
|
||||
tlsConfig.MinVersion = version
|
||||
}
|
||||
if version := toTLSVersion(setting.SSLMaximumVersion); version != 0 {
|
||||
tlsConfig.MaxVersion = version
|
||||
}
|
||||
|
||||
// Set curve preferences
|
||||
tlsConfig.CurvePreferences = []tls.CurveID{
|
||||
tls.X25519,
|
||||
tls.CurveP256,
|
||||
}
|
||||
if curves := toCurvePreferences(setting.SSLCurvePreferences); len(curves) > 0 {
|
||||
tlsConfig.CurvePreferences = curves
|
||||
}
|
||||
|
||||
// Set cipher suites
|
||||
tlsConfig.CipherSuites = defaultCiphers()
|
||||
if ciphers := toTLSCiphers(setting.SSLCipherSuites); len(ciphers) > 0 {
|
||||
tlsConfig.CipherSuites = ciphers
|
||||
}
|
||||
|
||||
tlsConfig.Certificates = make([]tls.Certificate, 1)
|
||||
|
||||
certPEMBlock, err := os.ReadFile(certFile)
|
||||
if err != nil {
|
||||
log.Error("Failed to load https cert file %s for %s:%s: %v", certFile, network, listenAddr, err)
|
||||
return err
|
||||
}
|
||||
|
||||
keyPEMBlock, err := os.ReadFile(keyFile)
|
||||
if err != nil {
|
||||
log.Error("Failed to load https key file %s for %s:%s: %v", keyFile, network, listenAddr, err)
|
||||
return err
|
||||
}
|
||||
|
||||
tlsConfig.Certificates[0], err = tls.X509KeyPair(certPEMBlock, keyPEMBlock)
|
||||
if err != nil {
|
||||
log.Error("Failed to create certificate from cert file %s and key file %s for %s:%s: %v", certFile, keyFile, network, listenAddr, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m)
|
||||
}
|
||||
|
||||
func runHTTPSWithTLSConfig(network, listenAddr, name string, tlsConfig *tls.Config, m http.Handler) error {
|
||||
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m)
|
||||
}
|
@@ -1,100 +0,0 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
)
|
||||
|
||||
func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler) error {
|
||||
|
||||
// If HTTP Challenge enabled, needs to be serving on port 80. For TLSALPN needs 443.
|
||||
// Due to docker port mapping this can't be checked programmatically
|
||||
// TODO: these are placeholders until we add options for each in settings with appropriate warning
|
||||
enableHTTPChallenge := true
|
||||
enableTLSALPNChallenge := true
|
||||
altHTTPPort := 0
|
||||
altTLSALPNPort := 0
|
||||
|
||||
if p, err := strconv.Atoi(setting.PortToRedirect); err == nil {
|
||||
altHTTPPort = p
|
||||
}
|
||||
if p, err := strconv.Atoi(setting.HTTPPort); err == nil {
|
||||
altTLSALPNPort = p
|
||||
}
|
||||
|
||||
magic := certmagic.NewDefault()
|
||||
magic.Storage = &certmagic.FileStorage{Path: directory}
|
||||
myACME := certmagic.NewACMEManager(magic, certmagic.ACMEManager{
|
||||
Email: email,
|
||||
Agreed: setting.LetsEncryptTOS,
|
||||
DisableHTTPChallenge: !enableHTTPChallenge,
|
||||
DisableTLSALPNChallenge: !enableTLSALPNChallenge,
|
||||
ListenHost: setting.HTTPAddr,
|
||||
AltTLSALPNPort: altTLSALPNPort,
|
||||
AltHTTPPort: altHTTPPort,
|
||||
})
|
||||
|
||||
magic.Issuers = []certmagic.Issuer{myACME}
|
||||
|
||||
// this obtains certificates or renews them if necessary
|
||||
err := magic.ManageSync(graceful.GetManager().HammerContext(), []string{domain})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tlsConfig := magic.TLSConfig()
|
||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
|
||||
|
||||
if version := toTLSVersion(setting.SSLMinimumVersion); version != 0 {
|
||||
tlsConfig.MinVersion = version
|
||||
}
|
||||
if version := toTLSVersion(setting.SSLMaximumVersion); version != 0 {
|
||||
tlsConfig.MaxVersion = version
|
||||
}
|
||||
|
||||
// Set curve preferences
|
||||
if curves := toCurvePreferences(setting.SSLCurvePreferences); len(curves) > 0 {
|
||||
tlsConfig.CurvePreferences = curves
|
||||
}
|
||||
|
||||
// Set cipher suites
|
||||
if ciphers := toTLSCiphers(setting.SSLCipherSuites); len(ciphers) > 0 {
|
||||
tlsConfig.CipherSuites = ciphers
|
||||
}
|
||||
|
||||
if enableHTTPChallenge {
|
||||
go func() {
|
||||
log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
|
||||
// all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
|
||||
var err = runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)))
|
||||
if err != nil {
|
||||
log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return runHTTPSWithTLSConfig("tcp", listenAddr, "Web", tlsConfig, m)
|
||||
}
|
||||
|
||||
func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "GET" && r.Method != "HEAD" {
|
||||
http.Error(w, "Use HTTPS", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// Remove the trailing slash at the end of setting.AppURL, the request
|
||||
// URI always contains a leading slash, which would result in a double
|
||||
// slash
|
||||
target := strings.TrimSuffix(setting.AppURL, "/") + r.URL.RequestURI()
|
||||
http.Redirect(w, r, target, http.StatusFound)
|
||||
}
|
@@ -22,13 +22,11 @@ The environment variables should be of the form:
|
||||
|
||||
GITEA__SECTION_NAME__KEY_NAME
|
||||
|
||||
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:
|
||||
"_0X2E_" for ".". The entire section and key names can be escaped as
|
||||
a UTF8 byte string if necessary. E.g. to configure:
|
||||
|
||||
"""
|
||||
...
|
||||
@@ -42,6 +40,27 @@ You would set the environment variables: "GITEA__LOG_0x2E_CONSOLE__COLORIZE=fals
|
||||
and "GITEA__LOG_0x2E_CONSOLE__STDERR=false". Other examples can be found
|
||||
on the configuration cheat sheet.
|
||||
|
||||
To build locally, run:
|
||||
To plug this command in to the docker, you simply compile the provided go file using:
|
||||
|
||||
go build environment-to-ini.go
|
||||
|
||||
And copy the resulting `environment-to-ini` command to /app/gitea in the docker.
|
||||
|
||||
Apply the below patch to /etc/s6/gitea.setup to wire this in.
|
||||
|
||||
If you find this useful please comment on #7287
|
||||
|
||||
|
||||
diff --git a/docker/root/etc/s6/gitea/setup b/docker/root/etc/s6/gitea/setup
|
||||
index f87ce9115..565bfcba9 100755
|
||||
--- a/docker/root/etc/s6/gitea/setup
|
||||
+++ b/docker/root/etc/s6/gitea/setup
|
||||
@@ -44,6 +44,8 @@ if [ ! -f ${GITEA_CUSTOM}/conf/app.ini ]; then
|
||||
SECRET_KEY=${SECRET_KEY:-""} \
|
||||
envsubst < /etc/templates/app.ini > ${GITEA_CUSTOM}/conf/app.ini
|
||||
|
||||
+ /app/gitea/environment-to-ini -c ${GITEA_CUSTOM}/conf/app.ini
|
||||
+
|
||||
chown ${USER}:git ${GITEA_CUSTOM}/conf/app.ini
|
||||
fi
|
||||
|
||||
go build contrib/environment-to-ini/environment-to-ini.go
|
||||
|
@@ -12,8 +12,8 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/unknwon/com"
|
||||
"github.com/urfave/cli"
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
@@ -97,11 +97,7 @@ func runEnvironmentToIni(c *cli.Context) error {
|
||||
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 com.IsFile(setting.CustomConf) {
|
||||
if err := cfg.Append(setting.CustomConf); err != nil {
|
||||
log.Fatal("Failed to load custom conf '%s': %v", setting.CustomConf, err)
|
||||
}
|
||||
@@ -110,8 +106,6 @@ func runEnvironmentToIni(c *cli.Context) error {
|
||||
}
|
||||
cfg.NameMapper = ini.SnackCase
|
||||
|
||||
changed := false
|
||||
|
||||
prefix := c.String("prefix") + "__"
|
||||
|
||||
for _, kv := range os.Environ() {
|
||||
@@ -145,22 +139,15 @@ func runEnvironmentToIni(c *cli.Context) error {
|
||||
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 {
|
||||
log.Info("Settings saved to: %q", destination)
|
||||
err = cfg.SaveTo(destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err := cfg.SaveTo(destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.Bool("clear") {
|
||||
for _, kv := range os.Environ() {
|
||||
@@ -225,6 +212,7 @@ func DecodeSectionKey(encoded string) (string, string) {
|
||||
if !inKey {
|
||||
if splitter := strings.Index(remaining, "__"); splitter > -1 {
|
||||
section += remaining[:splitter]
|
||||
inKey = true
|
||||
key += remaining[splitter+2:]
|
||||
} else {
|
||||
section += remaining
|
||||
@@ -232,6 +220,5 @@ func DecodeSectionKey(encoded string) (string, string) {
|
||||
} else {
|
||||
key += remaining
|
||||
}
|
||||
section = strings.ToLower(section)
|
||||
return section, key
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
#############################################################################
|
||||
# This script sets some defaults for gitea to run in a FHS compliant manner #
|
||||
#############################################################################
|
||||
########################################################################
|
||||
# This script some defaults for gitea to run in a FHS compliant manner #
|
||||
########################################################################
|
||||
|
||||
# It assumes that you place this script as gitea in /usr/bin
|
||||
#
|
||||
@@ -36,7 +36,7 @@ if [ -z "$APP_INI_SET" ]; then
|
||||
CONF_ARG="-c \"$APP_INI\""
|
||||
fi
|
||||
|
||||
# Provide FHS compliant defaults
|
||||
GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" exec -a "$0" "$GITEA" $CONF_ARG "$@"
|
||||
# Provide FHS compliant defaults to
|
||||
GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" "$GITEA" $CONF_ARG "$@"
|
||||
|
||||
|
||||
|
@@ -6,11 +6,11 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
)
|
||||
|
||||
// To generate derivative fixtures, execute the following from Gitea's repository base dir:
|
||||
@@ -31,13 +31,11 @@ var (
|
||||
func main() {
|
||||
pathToGiteaRoot := "."
|
||||
fixturesDir = filepath.Join(pathToGiteaRoot, "models", "fixtures")
|
||||
if err := unittest.CreateTestEngine(unittest.FixturesOptions{
|
||||
Dir: fixturesDir,
|
||||
}); err != nil {
|
||||
if err := models.CreateTestEngine(fixturesDir); err != nil {
|
||||
fmt.Printf("CreateTestEngine: %+v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := unittest.PrepareTestDatabase(); err != nil {
|
||||
if err := models.PrepareTestDatabase(); err != nil {
|
||||
fmt.Printf("PrepareTestDatabase: %+v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -66,7 +64,7 @@ func generate(name string) error {
|
||||
return err
|
||||
}
|
||||
path := filepath.Join(fixturesDir, name+".yml")
|
||||
if err := os.WriteFile(path, []byte(data), 0644); err != nil {
|
||||
if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil {
|
||||
return fmt.Errorf("%s: %+v", path, err)
|
||||
}
|
||||
fmt.Printf("%s created.\n", path)
|
||||
|
2
contrib/gitea-monitoring-mixin/.gitignore
vendored
2
contrib/gitea-monitoring-mixin/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
dashboards_out
|
||||
vendor
|
@@ -1,31 +0,0 @@
|
||||
JSONNET_FMT := jsonnetfmt -n 2 --max-blank-lines 1 --string-style s --comment-style s
|
||||
|
||||
.PHONY: all
|
||||
all: build dashboards_out
|
||||
|
||||
vendor: jsonnetfile.json
|
||||
jb install
|
||||
|
||||
.PHONY: build
|
||||
build: vendor
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \
|
||||
xargs -n 1 -- $(JSONNET_FMT) -i
|
||||
|
||||
.PHONY: lint
|
||||
lint: build
|
||||
find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \
|
||||
while read f; do \
|
||||
$(JSONNET_FMT) "$$f" | diff -u "$$f" -; \
|
||||
done
|
||||
mixtool lint mixin.libsonnet
|
||||
|
||||
dashboards_out: mixin.libsonnet config.libsonnet $(wildcard dashboards/*)
|
||||
@mkdir -p dashboards_out
|
||||
jsonnet -J vendor -m dashboards_out lib/dashboards.jsonnet
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf dashboards_out
|
@@ -1,33 +0,0 @@
|
||||
# Gitea Mixin
|
||||
|
||||
Gitea Mixin is a set of configurable Grafana dashboards based on the metrics exported by the Gitea built-in metrics endpoint.
|
||||
|
||||
## Generate config files
|
||||
|
||||
You can manually generate dashboards, but first you should install some tools:
|
||||
|
||||
```bash
|
||||
go install github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb@latest
|
||||
go install github.com/google/go-jsonnet/cmd/jsonnet@latest
|
||||
# or in brew: brew install go-jsonnet
|
||||
```
|
||||
|
||||
For linting and formatting, you would also need `mixtool` and `jsonnetfmt` installed. If you
|
||||
have a working Go development environment, it's easiest to run the following:
|
||||
|
||||
```bash
|
||||
go install github.com/monitoring-mixins/mixtool/cmd/mixtool@latest
|
||||
go install github.com/google/go-jsonnet/cmd/jsonnetfmt@latest
|
||||
```
|
||||
|
||||
The files in `dashboards_out` need to be imported
|
||||
into your Grafana server. The exact details will be depending on your environment.
|
||||
|
||||
Edit `config.libsonnet` if required and then build JSON dashboard files for Grafana:
|
||||
|
||||
```bash
|
||||
make
|
||||
```
|
||||
|
||||
For more advanced uses of mixins, see
|
||||
https://github.com/monitoring-mixins/docs.
|
@@ -1,99 +0,0 @@
|
||||
{
|
||||
_config+:: {
|
||||
local c = self,
|
||||
dashboardNamePrefix: 'Gitea',
|
||||
dashboardTags: ['gitea'],
|
||||
dashboardPeriod: 'now-1h',
|
||||
dashboardTimezone: 'default',
|
||||
dashboardRefresh: '1m',
|
||||
|
||||
// please see https://docs.gitea.io/en-us/config-cheat-sheet/#metrics-metrics
|
||||
// Show issue by repository metrics with format gitea_issues_by_repository{repository="org/repo"} 5.
|
||||
// Requires Gitea 1.16.0 with ENABLED_ISSUE_BY_REPOSITORY set to true.
|
||||
showIssuesByRepository: true,
|
||||
// Show graphs for issue by label metrics with format gitea_issues_by_label{label="bug"} 2.
|
||||
// Requires Gitea 1.16.0 with ENABLED_ISSUE_BY_LABEL set to true.
|
||||
showIssuesByLabel: true,
|
||||
|
||||
// Requires Gitea 1.16.0.
|
||||
showIssuesOpenClose: true,
|
||||
|
||||
// add or remove metrics from dashboard
|
||||
giteaStatMetrics:
|
||||
[
|
||||
{
|
||||
name: 'gitea_organizations',
|
||||
description: 'Organizations',
|
||||
},
|
||||
{
|
||||
name: 'gitea_teams',
|
||||
description: 'Teams',
|
||||
},
|
||||
{
|
||||
name: 'gitea_users',
|
||||
description: 'Users',
|
||||
},
|
||||
{
|
||||
name: 'gitea_repositories',
|
||||
description: 'Repositories',
|
||||
},
|
||||
{
|
||||
name: 'gitea_milestones',
|
||||
description: 'Milestones',
|
||||
},
|
||||
{
|
||||
name: 'gitea_stars',
|
||||
description: 'Stars',
|
||||
},
|
||||
{
|
||||
name: 'gitea_releases',
|
||||
description: 'Releases',
|
||||
},
|
||||
]
|
||||
+
|
||||
if c.showIssuesOpenClose then
|
||||
[
|
||||
{
|
||||
name: 'gitea_issues_open',
|
||||
description: 'Issues opened',
|
||||
},
|
||||
{
|
||||
name: 'gitea_issues_closed',
|
||||
description: 'Issues closed',
|
||||
},
|
||||
] else
|
||||
[
|
||||
{
|
||||
name: 'gitea_issues',
|
||||
description: 'Issues',
|
||||
},
|
||||
],
|
||||
//set this for using label colors on graphs
|
||||
issueLabels: [
|
||||
{
|
||||
label: 'bug',
|
||||
color: '#ee0701',
|
||||
},
|
||||
{
|
||||
label: 'duplicate',
|
||||
color: '#cccccc',
|
||||
},
|
||||
{
|
||||
label: 'invalid',
|
||||
color: '#e6e6e6',
|
||||
},
|
||||
{
|
||||
label: 'enhancement',
|
||||
color: '#84b6eb',
|
||||
},
|
||||
{
|
||||
label: 'help wanted',
|
||||
color: '#128a0c',
|
||||
},
|
||||
{
|
||||
label: 'question',
|
||||
color: '#cc317c',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
@@ -1 +0,0 @@
|
||||
(import 'overview.libsonnet')
|
@@ -1,461 +0,0 @@
|
||||
local grafana = import 'github.com/grafana/grafonnet-lib/grafonnet/grafana.libsonnet';
|
||||
local prometheus = grafana.prometheus;
|
||||
|
||||
local addIssueLabelsOverrides(labels) =
|
||||
{
|
||||
fieldConfig+: {
|
||||
overrides+: [
|
||||
{
|
||||
matcher: {
|
||||
id: 'byRegexp',
|
||||
options: label.label,
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
id: 'color',
|
||||
value: {
|
||||
fixedColor: label.color,
|
||||
mode: 'fixed',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
for label in labels
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
{
|
||||
|
||||
grafanaDashboards+:: {
|
||||
|
||||
local giteaSelector = 'job="$job", instance="$instance"',
|
||||
local giteaStatsPanel =
|
||||
grafana.statPanel.new(
|
||||
'Gitea stats',
|
||||
datasource='$datasource',
|
||||
reducerFunction='lastNotNull',
|
||||
graphMode='none',
|
||||
colorMode='value',
|
||||
)
|
||||
.addTargets(
|
||||
[
|
||||
prometheus.target(expr='%s{%s}' % [metric.name, giteaSelector], legendFormat=metric.description, intervalFactor=10)
|
||||
for metric in $._config.giteaStatMetrics
|
||||
]
|
||||
)
|
||||
+ {
|
||||
fieldConfig+: {
|
||||
defaults+: {
|
||||
color: {
|
||||
fixedColor: 'blue',
|
||||
mode: 'fixed',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
local giteaUptimePanel =
|
||||
grafana.statPanel.new(
|
||||
'Uptime',
|
||||
datasource='$datasource',
|
||||
reducerFunction='last',
|
||||
graphMode='area',
|
||||
colorMode='value',
|
||||
)
|
||||
.addTarget(prometheus.target(expr='time()-process_start_time_seconds{%s}' % giteaSelector, intervalFactor=1))
|
||||
+ {
|
||||
fieldConfig+: {
|
||||
defaults+: {
|
||||
color: {
|
||||
fixedColor: 'blue',
|
||||
mode: 'fixed',
|
||||
},
|
||||
unit: 's',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
local giteaMemoryPanel =
|
||||
grafana.graphPanel.new(
|
||||
'Memory usage',
|
||||
datasource='$datasource'
|
||||
)
|
||||
.addTarget(prometheus.target(expr='process_resident_memory_bytes{%s}' % giteaSelector, intervalFactor=2))
|
||||
+ {
|
||||
type: 'timeseries',
|
||||
options+: {
|
||||
tooltip: {
|
||||
mode: 'multi',
|
||||
},
|
||||
legend+: {
|
||||
displayMode: 'hidden',
|
||||
},
|
||||
},
|
||||
fieldConfig+: {
|
||||
defaults+: {
|
||||
custom+: {
|
||||
lineInterpolation: 'smooth',
|
||||
fillOpacity: 15,
|
||||
},
|
||||
color: {
|
||||
fixedColor: 'green',
|
||||
mode: 'fixed',
|
||||
},
|
||||
unit: 'decbytes',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
local giteaCpuPanel =
|
||||
grafana.graphPanel.new(
|
||||
'CPU usage',
|
||||
datasource='$datasource'
|
||||
)
|
||||
.addTarget(prometheus.target(expr='rate(process_cpu_seconds_total{%s}[$__rate_interval])*100' % giteaSelector, intervalFactor=2))
|
||||
+ {
|
||||
type: 'timeseries',
|
||||
options+: {
|
||||
tooltip: {
|
||||
mode: 'multi',
|
||||
},
|
||||
legend+: {
|
||||
displayMode: 'hidden',
|
||||
},
|
||||
},
|
||||
fieldConfig+: {
|
||||
defaults+: {
|
||||
custom+: {
|
||||
lineInterpolation: 'smooth',
|
||||
gradientMode: 'scheme',
|
||||
fillOpacity: 15,
|
||||
axisSoftMin: 0,
|
||||
axisSoftMax: 0,
|
||||
},
|
||||
color: {
|
||||
mode: 'continuous-GrYlRd', // from green to red (100%)
|
||||
},
|
||||
unit: 'percent',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
matcher: {
|
||||
id: 'byRegexp',
|
||||
options: '.+',
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
id: 'max',
|
||||
value: 100,
|
||||
},
|
||||
{
|
||||
id: 'min',
|
||||
value: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
local giteaFileDescriptorsPanel =
|
||||
grafana.graphPanel.new(
|
||||
'File descriptors usage',
|
||||
datasource='$datasource',
|
||||
)
|
||||
.addTarget(prometheus.target(expr='process_open_fds{%s}' % giteaSelector, intervalFactor=2))
|
||||
.addTarget(prometheus.target(expr='process_max_fds{%s}' % giteaSelector, intervalFactor=2))
|
||||
.addSeriesOverride(
|
||||
{
|
||||
alias: '/process_max_fds.+/',
|
||||
color: '#F2495C', // red
|
||||
dashes: true,
|
||||
fill: 0,
|
||||
},
|
||||
)
|
||||
+ {
|
||||
type: 'timeseries',
|
||||
options+: {
|
||||
tooltip: {
|
||||
mode: 'multi',
|
||||
},
|
||||
legend+: {
|
||||
displayMode: 'hidden',
|
||||
},
|
||||
},
|
||||
fieldConfig+: {
|
||||
defaults+: {
|
||||
custom+: {
|
||||
lineInterpolation: 'smooth',
|
||||
gradientMode: 'scheme',
|
||||
fillOpacity: 0,
|
||||
},
|
||||
color: {
|
||||
fixedColor: 'green',
|
||||
mode: 'fixed',
|
||||
},
|
||||
unit: '',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
matcher: {
|
||||
id: 'byFrameRefID',
|
||||
options: 'B',
|
||||
},
|
||||
properties: [
|
||||
{
|
||||
id: 'custom.lineStyle',
|
||||
value: {
|
||||
fill: 'dash',
|
||||
dash: [
|
||||
10,
|
||||
10,
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'color',
|
||||
value: {
|
||||
mode: 'fixed',
|
||||
fixedColor: 'red',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
local giteaChangesPanelPrototype =
|
||||
grafana.graphPanel.new(
|
||||
'',
|
||||
datasource='$datasource',
|
||||
interval='$agg_interval',
|
||||
maxDataPoints=10000,
|
||||
)
|
||||
+ {
|
||||
type: 'timeseries',
|
||||
options+: {
|
||||
tooltip: {
|
||||
mode: 'multi',
|
||||
},
|
||||
legend+: {
|
||||
calcs+: [
|
||||
'sum',
|
||||
],
|
||||
},
|
||||
},
|
||||
fieldConfig+: {
|
||||
defaults+: {
|
||||
noValue: '0',
|
||||
custom+: {
|
||||
drawStyle: 'bars',
|
||||
barAlignment: -1,
|
||||
fillOpacity: 50,
|
||||
gradientMode: 'hue',
|
||||
pointSize: 1,
|
||||
lineWidth: 0,
|
||||
stacking: {
|
||||
group: 'A',
|
||||
mode: 'normal',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
local giteaChangesPanelAll =
|
||||
giteaChangesPanelPrototype
|
||||
.addTarget(prometheus.target(expr='changes(process_start_time_seconds{%s}[$__interval]) > 0' % [giteaSelector], legendFormat='Restarts', intervalFactor=1))
|
||||
.addTargets(
|
||||
[
|
||||
prometheus.target(expr='floor(delta(%s{%s}[$__interval])) > 0' % [metric.name, giteaSelector], legendFormat=metric.description, intervalFactor=1)
|
||||
for metric in $._config.giteaStatMetrics
|
||||
]
|
||||
) + { id: 200 }, // some unique number, beyond the maximum number of panels in the dashboard,
|
||||
|
||||
local giteaChangesPanelTotal =
|
||||
grafana.statPanel.new(
|
||||
'Changes',
|
||||
datasource='-- Dashboard --',
|
||||
reducerFunction='sum',
|
||||
graphMode='none',
|
||||
textMode='value_and_name',
|
||||
colorMode='value',
|
||||
)
|
||||
+ {
|
||||
targets+: [
|
||||
{
|
||||
panelId: giteaChangesPanelAll.id,
|
||||
refId: 'A',
|
||||
},
|
||||
],
|
||||
}
|
||||
+ {
|
||||
fieldConfig+: {
|
||||
defaults+: {
|
||||
color: {
|
||||
mode: 'palette-classic',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
local giteaChangesByRepositories =
|
||||
giteaChangesPanelPrototype
|
||||
.addTarget(prometheus.target(expr='floor(increase(gitea_issues_by_repository{%s}[$__interval])) > 0' % [giteaSelector], legendFormat='{{ repository }}', intervalFactor=1))
|
||||
+ { id: 210 }, // some unique number, beyond the maximum number of panels in the dashboard,
|
||||
|
||||
local giteaChangesByRepositoriesTotal =
|
||||
grafana.statPanel.new(
|
||||
'Issues by repository',
|
||||
datasource='-- Dashboard --',
|
||||
reducerFunction='sum',
|
||||
graphMode='none',
|
||||
textMode='value_and_name',
|
||||
colorMode='value',
|
||||
)
|
||||
+ {
|
||||
id: 211,
|
||||
targets+: [
|
||||
{
|
||||
panelId: giteaChangesByRepositories.id,
|
||||
refId: 'A',
|
||||
},
|
||||
],
|
||||
}
|
||||
+ {
|
||||
fieldConfig+: {
|
||||
defaults+: {
|
||||
color: {
|
||||
mode: 'palette-classic',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
local giteaChangesByLabel =
|
||||
giteaChangesPanelPrototype
|
||||
.addTarget(prometheus.target(expr='floor(increase(gitea_issues_by_label{%s}[$__interval])) > 0' % [giteaSelector], legendFormat='{{ label }}', intervalFactor=1))
|
||||
+ addIssueLabelsOverrides($._config.issueLabels)
|
||||
+ { id: 220 }, // some unique number, beyond the maximum number of panels in the dashboard,
|
||||
|
||||
local giteaChangesByLabelTotal =
|
||||
grafana.statPanel.new(
|
||||
'Issues by labels',
|
||||
datasource='-- Dashboard --',
|
||||
reducerFunction='sum',
|
||||
graphMode='none',
|
||||
textMode='value_and_name',
|
||||
colorMode='value',
|
||||
)
|
||||
+ addIssueLabelsOverrides($._config.issueLabels)
|
||||
+ {
|
||||
id: 221,
|
||||
targets+: [
|
||||
{
|
||||
panelId: giteaChangesByLabel.id,
|
||||
refId: 'A',
|
||||
},
|
||||
],
|
||||
}
|
||||
+ {
|
||||
fieldConfig+: {
|
||||
defaults+: {
|
||||
color: {
|
||||
mode: 'palette-classic',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
'gitea-overview.json':
|
||||
grafana.dashboard.new(
|
||||
'%s Overview' % $._config.dashboardNamePrefix,
|
||||
time_from='%s' % $._config.dashboardPeriod,
|
||||
editable=false,
|
||||
tags=($._config.dashboardTags),
|
||||
timezone='%s' % $._config.dashboardTimezone,
|
||||
refresh='%s' % $._config.dashboardRefresh,
|
||||
graphTooltip='shared_crosshair',
|
||||
uid='gitea-overview'
|
||||
)
|
||||
.addTemplate(
|
||||
{
|
||||
current: {
|
||||
text: 'Prometheus',
|
||||
value: 'Prometheus',
|
||||
},
|
||||
hide: 0,
|
||||
label: 'Data Source',
|
||||
name: 'datasource',
|
||||
options: [],
|
||||
query: 'prometheus',
|
||||
refresh: 1,
|
||||
regex: '',
|
||||
type: 'datasource',
|
||||
},
|
||||
)
|
||||
.addTemplate(
|
||||
{
|
||||
hide: 0,
|
||||
label: null,
|
||||
name: 'job',
|
||||
options: [],
|
||||
query: 'label_values(gitea_organizations, job)',
|
||||
refresh: 1,
|
||||
regex: '',
|
||||
type: 'query',
|
||||
},
|
||||
)
|
||||
.addTemplate(
|
||||
{
|
||||
hide: 0,
|
||||
label: null,
|
||||
name: 'instance',
|
||||
options: [],
|
||||
query: 'label_values(gitea_organizations{job="$job"}, instance)',
|
||||
refresh: 1,
|
||||
regex: '',
|
||||
type: 'query',
|
||||
},
|
||||
)
|
||||
.addTemplate(
|
||||
{
|
||||
hide: 0,
|
||||
label: 'aggregation interval',
|
||||
name: 'agg_interval',
|
||||
auto_min: '1m',
|
||||
auto: true,
|
||||
query: '1m,10m,1h,1d,7d',
|
||||
type: 'interval',
|
||||
},
|
||||
)
|
||||
.addPanel(grafana.row.new(title='General'), gridPos={ x: 0, y: 0, w: 0, h: 0 },)
|
||||
.addPanel(giteaStatsPanel, gridPos={ x: 0, y: 0, w: 16, h: 4 })
|
||||
.addPanel(giteaUptimePanel, gridPos={ x: 16, y: 0, w: 8, h: 4 })
|
||||
.addPanel(giteaMemoryPanel, gridPos={ x: 0, y: 4, w: 8, h: 6 })
|
||||
.addPanel(giteaCpuPanel, gridPos={ x: 8, y: 4, w: 8, h: 6 })
|
||||
.addPanel(giteaFileDescriptorsPanel, gridPos={ x: 16, y: 4, w: 8, h: 6 })
|
||||
.addPanel(grafana.row.new(title='Changes', collapse=false), gridPos={ x: 0, y: 10, w: 24, h: 8 })
|
||||
.addPanel(giteaChangesPanelTotal, gridPos={ x: 0, y: 12, w: 6, h: 8 })
|
||||
+ // use patching instead of .addPanel() to keep static ids
|
||||
{
|
||||
panels+: std.flattenArrays([
|
||||
[
|
||||
giteaChangesPanelAll { gridPos: { x: 6, y: 12, w: 18, h: 8 } },
|
||||
],
|
||||
if $._config.showIssuesByRepository then
|
||||
[
|
||||
giteaChangesByRepositoriesTotal { gridPos: { x: 0, y: 20, w: 6, h: 8 } },
|
||||
giteaChangesByRepositories { gridPos: { x: 6, y: 20, w: 18, h: 8 } },
|
||||
] else [],
|
||||
if $._config.showIssuesByLabel then
|
||||
[
|
||||
giteaChangesByLabelTotal { gridPos: { x: 0, y: 28, w: 6, h: 8 } },
|
||||
giteaChangesByLabel { gridPos: { x: 6, y: 28, w: 18, h: 8 } },
|
||||
] else [],
|
||||
]),
|
||||
},
|
||||
},
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": [
|
||||
{
|
||||
"source": {
|
||||
"git": {
|
||||
"remote": "https://github.com/grafana/grafonnet-lib.git",
|
||||
"subdir": "grafonnet"
|
||||
}
|
||||
},
|
||||
"version": "master"
|
||||
}
|
||||
],
|
||||
"legacyImports": false
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": [
|
||||
{
|
||||
"source": {
|
||||
"git": {
|
||||
"remote": "https://github.com/grafana/grafonnet-lib.git",
|
||||
"subdir": "grafonnet"
|
||||
}
|
||||
},
|
||||
"version": "3626fc4dc2326931c530861ac5bebe39444f6cbf",
|
||||
"sum": "gF8foHByYcB25jcUOBqP6jxk0OPifQMjPvKY0HaCk6w="
|
||||
}
|
||||
],
|
||||
"legacyImports": false
|
||||
}
|
@@ -1 +0,0 @@
|
||||
std.manifestYamlDoc((import '../mixin.libsonnet').prometheusAlerts)
|
@@ -1,6 +0,0 @@
|
||||
local dashboards = (import '../mixin.libsonnet').grafanaDashboards;
|
||||
|
||||
{
|
||||
[name]: dashboards[name]
|
||||
for name in std.objectFields(dashboards)
|
||||
}
|
@@ -1 +0,0 @@
|
||||
std.manifestYamlDoc((import '../mixin.libsonnet').prometheusRules)
|
@@ -1,2 +0,0 @@
|
||||
(import 'dashboards/dashboards.libsonnet') +
|
||||
(import 'config.libsonnet')
|
@@ -19,7 +19,7 @@
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"buildFlags": "-tags='sqlite sqlite_unlock_notify'",
|
||||
"buildFlags": "-tags=\"sqlite sqlite_unlock_notify\"",
|
||||
"port": 2345,
|
||||
"host": "127.0.0.1",
|
||||
"program": "${workspaceRoot}/main.go",
|
||||
|
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"go.buildTags": "'sqlite sqlite_unlock_notify'",
|
||||
"go.testFlags": ["-v"]
|
||||
}
|
@@ -12,6 +12,7 @@ import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -25,18 +26,18 @@ import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
gitea_git "code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/external"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/routers"
|
||||
"code.gitea.io/gitea/routers/routes"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/config"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
context2 "github.com/gorilla/context"
|
||||
"github.com/unknwon/com"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
@@ -49,13 +50,13 @@ func runPR() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
setting.SetCustomPathAndConf("", "", "")
|
||||
setting.LoadAllowEmpty()
|
||||
setting.NewContext()
|
||||
|
||||
setting.RepoRootPath, err = os.MkdirTemp(os.TempDir(), "repos")
|
||||
setting.RepoRootPath, err = ioutil.TempDir(os.TempDir(), "repos")
|
||||
if err != nil {
|
||||
log.Fatalf("TempDir: %v\n", err)
|
||||
}
|
||||
setting.AppDataPath, err = os.MkdirTemp(os.TempDir(), "appdata")
|
||||
setting.AppDataPath, err = ioutil.TempDir(os.TempDir(), "appdata")
|
||||
if err != nil {
|
||||
log.Fatalf("TempDir: %v\n", err)
|
||||
}
|
||||
@@ -80,45 +81,44 @@ func runPR() {
|
||||
setting.RunUser = curUser.Username
|
||||
|
||||
log.Printf("[PR] Loading fixtures data ...\n")
|
||||
gitea_git.CheckLFSVersion()
|
||||
setting.CheckLFSVersion()
|
||||
//models.LoadConfigs()
|
||||
/*
|
||||
setting.Database.Type = "sqlite3"
|
||||
setting.Database.Path = ":memory:"
|
||||
setting.Database.Timeout = 500
|
||||
*/
|
||||
dbCfg := setting.Cfg.Section("database")
|
||||
dbCfg.NewKey("DB_TYPE", "sqlite3")
|
||||
dbCfg.NewKey("PATH", ":memory:")
|
||||
db := setting.Cfg.Section("database")
|
||||
db.NewKey("DB_TYPE", "sqlite3")
|
||||
db.NewKey("PATH", ":memory:")
|
||||
|
||||
routers.InitGitServices()
|
||||
routers.NewServices()
|
||||
setting.Database.LogSQL = true
|
||||
//x, err = xorm.NewEngine("sqlite3", "file::memory:?cache=shared")
|
||||
|
||||
db.InitEngineWithMigration(context.Background(), func(_ *xorm.Engine) error {
|
||||
models.NewEngine(context.Background(), func(_ *xorm.Engine) error {
|
||||
return nil
|
||||
})
|
||||
db.HasEngine = true
|
||||
models.HasEngine = true
|
||||
//x.ShowSQL(true)
|
||||
err = unittest.InitFixtures(
|
||||
unittest.FixturesOptions{
|
||||
Dir: path.Join(curDir, "models/fixtures/"),
|
||||
},
|
||||
err = models.InitFixtures(
|
||||
path.Join(curDir, "models/fixtures/"),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Printf("Error initializing test database: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
unittest.LoadFixtures()
|
||||
models.LoadFixtures()
|
||||
util.RemoveAll(setting.RepoRootPath)
|
||||
util.RemoveAll(models.LocalCopyPath())
|
||||
util.CopyDir(path.Join(curDir, "integrations/gitea-repositories-meta"), setting.RepoRootPath)
|
||||
com.CopyDir(path.Join(curDir, "integrations/gitea-repositories-meta"), setting.RepoRootPath)
|
||||
|
||||
log.Printf("[PR] Setting up router\n")
|
||||
//routers.GlobalInit()
|
||||
external.RegisterRenderers()
|
||||
external.RegisterParsers()
|
||||
markup.Init()
|
||||
c := routers.NormalRoutes()
|
||||
m := routes.NewMacaron()
|
||||
routes.RegisterRoutes(m)
|
||||
|
||||
log.Printf("[PR] Ready for testing !\n")
|
||||
log.Printf("[PR] Login with user1, user2, user3, ... with pass: password\n")
|
||||
@@ -138,7 +138,7 @@ func runPR() {
|
||||
*/
|
||||
|
||||
//Start the server
|
||||
http.ListenAndServe(":8080", c)
|
||||
http.ListenAndServe(":8080", context2.ClearHandler(m))
|
||||
|
||||
log.Printf("[PR] Cleaning up ...\n")
|
||||
/*
|
||||
@@ -182,7 +182,7 @@ func main() {
|
||||
codeFilePath = filepath.FromSlash(codeFilePath) //Convert to running OS
|
||||
|
||||
//Copy this file if it will not exist in the PR branch
|
||||
dat, err := os.ReadFile(codeFilePath)
|
||||
dat, err := ioutil.ReadFile(codeFilePath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to cache this code file : %v", err)
|
||||
}
|
||||
@@ -215,7 +215,7 @@ func main() {
|
||||
//Use git cli command for windows
|
||||
runCmd("git", "fetch", remoteUpstream, fmt.Sprintf("pull/%s/head:%s", pr, branch))
|
||||
} else {
|
||||
ref := fmt.Sprintf("%s%s/head:%s", gitea_git.PullPrefix, pr, branchRef)
|
||||
ref := fmt.Sprintf("refs/pull/%s/head:%s", pr, branchRef)
|
||||
err = repo.Fetch(&git.FetchOptions{
|
||||
RemoteName: remoteUpstream,
|
||||
RefSpecs: []config.RefSpec{
|
||||
@@ -246,7 +246,7 @@ func main() {
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to duplicate this code file in PR : %v", err)
|
||||
}
|
||||
err = os.WriteFile(codeFilePath, dat, 0644)
|
||||
err = ioutil.WriteFile(codeFilePath, dat, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to duplicate this code file in PR : %v", err)
|
||||
}
|
||||
|
@@ -3,23 +3,14 @@ Description=Gitea (Git with a cup of tea)
|
||||
After=syslog.target
|
||||
After=network.target
|
||||
###
|
||||
# Don't forget to add the database service dependencies
|
||||
# Don't forget to add the database service requirements
|
||||
###
|
||||
#
|
||||
#Wants=mysql.service
|
||||
#After=mysql.service
|
||||
#
|
||||
#Wants=mariadb.service
|
||||
#After=mariadb.service
|
||||
#
|
||||
#Wants=postgresql.service
|
||||
#After=postgresql.service
|
||||
#
|
||||
#Wants=memcached.service
|
||||
#After=memcached.service
|
||||
#
|
||||
#Wants=redis.service
|
||||
#After=redis.service
|
||||
#Requires=mysql.service
|
||||
#Requires=mariadb.service
|
||||
#Requires=postgresql.service
|
||||
#Requires=memcached.service
|
||||
#Requires=redis.service
|
||||
#
|
||||
###
|
||||
# If using socket activation for main http/s
|
||||
@@ -66,12 +57,6 @@ WorkingDirectory=/var/lib/gitea/
|
||||
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
|
||||
Restart=always
|
||||
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
|
||||
# If you install Git to directory prefix other than default PATH (which happens
|
||||
# for example if you install other versions of Git side-to-side with
|
||||
# distribution version), uncomment below line and add that prefix to PATH
|
||||
# Don't forget to place git-lfs binary on the PATH below if you want to enable
|
||||
# Git LFS support
|
||||
#Environment=PATH=/path/to/git/bin:/bin:/sbin:/usr/bin:/usr/sbin
|
||||
# If you want to bind Gitea to a port below 1024, uncomment
|
||||
# the two values below, or use socket activation to pass Gitea its ports as above
|
||||
###
|
||||
|
@@ -1,8 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
grep 'git' go.mod | grep '\.com' | grep -v indirect | grep -v replace | cut -f 2 | cut -d ' ' -f 1 | while read line; do
|
||||
go get -u "$line"
|
||||
make vendor
|
||||
git add .
|
||||
git commit -S -m "update $line"
|
||||
done
|
File diff suppressed because it is too large
Load Diff
@@ -1,20 +0,0 @@
|
||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}dev{{/if}}-rootless
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}-rootless
|
||||
{{/each}}
|
||||
- "latest-rootless"
|
||||
{{/if}}
|
||||
manifests:
|
||||
-
|
||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}dev{{/if}}-linux-amd64-rootless
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
-
|
||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}dev{{/if}}-linux-arm64-rootless
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
variant: v8
|
@@ -1,19 +1,18 @@
|
||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}dev{{/if}}
|
||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
- "latest"
|
||||
{{/if}}
|
||||
manifests:
|
||||
-
|
||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}dev{{/if}}-linux-amd64
|
||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
-
|
||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}{{#if (hasPrefix "refs/heads/release/v" build.ref)}}{{trimPrefix "refs/heads/release/v" build.ref}}-{{/if}}dev{{/if}}-linux-arm64
|
||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
[[ -f ./setup ]] && source ./setup
|
||||
|
||||
pushd /app/gitea >/dev/null
|
||||
exec su-exec $USER /usr/local/bin/gitea web
|
||||
pushd /app/gitea > /dev/null
|
||||
exec su-exec $USER /app/gitea/gitea web
|
||||
popd
|
||||
|
@@ -23,7 +23,7 @@ if [ ! -f ${GITEA_CUSTOM}/conf/app.ini ]; then
|
||||
INSTALL_LOCK=true
|
||||
fi
|
||||
|
||||
# Substitute the environment variables in the template
|
||||
# Substitude the environment variables in the template
|
||||
APP_NAME=${APP_NAME:-"Gitea: Git with a cup of tea"} \
|
||||
RUN_MODE=${RUN_MODE:-"prod"} \
|
||||
DOMAIN=${DOMAIN:-"localhost"} \
|
||||
@@ -48,9 +48,6 @@ if [ ! -f ${GITEA_CUSTOM}/conf/app.ini ]; then
|
||||
chown ${USER}:git ${GITEA_CUSTOM}/conf/app.ini
|
||||
fi
|
||||
|
||||
# Replace app.ini settings with env variables in the form GITEA__SECTION_NAME__KEY_NAME
|
||||
environment-to-ini --config ${GITEA_CUSTOM}/conf/app.ini
|
||||
|
||||
# only chown if current owner is not already the gitea ${USER}. No recursive check to save time
|
||||
if ! [[ $(ls -ld /data/gitea | awk '{print $3}') = ${USER} ]]; then chown -R ${USER}:git /data/gitea; fi
|
||||
if ! [[ $(ls -ld /app/gitea | awk '{print $3}') = ${USER} ]]; then chown -R ${USER}:git /app/gitea; fi
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
[[ -f ./setup ]] && source ./setup
|
||||
|
||||
pushd /root >/dev/null
|
||||
exec su-exec root /usr/sbin/sshd -D -e 2>&1
|
||||
pushd /root > /dev/null
|
||||
exec su-exec root /usr/sbin/sshd -D -e 2>&1
|
||||
popd
|
||||
|
@@ -24,31 +24,9 @@ if [ ! -f /data/ssh/ssh_host_ecdsa_key ]; then
|
||||
ssh-keygen -t ecdsa -b 256 -f /data/ssh/ssh_host_ecdsa_key -N "" > /dev/null
|
||||
fi
|
||||
|
||||
if [ -e /data/ssh/ssh_host_ed25519_cert ]; then
|
||||
SSH_ED25519_CERT=${SSH_ED25519_CERT:-"/data/ssh/ssh_host_ed25519_cert"}
|
||||
fi
|
||||
|
||||
if [ -e /data/ssh/ssh_host_rsa_cert ]; then
|
||||
SSH_RSA_CERT=${SSH_RSA_CERT:-"/data/ssh/ssh_host_rsa_cert"}
|
||||
fi
|
||||
|
||||
if [ -e /data/ssh/ssh_host_ecdsa_cert ]; then
|
||||
SSH_ECDSA_CERT=${SSH_ECDSA_CERT:-"/data/ssh/ssh_host_ecdsa_cert"}
|
||||
fi
|
||||
|
||||
if [ -e /data/ssh/ssh_host_dsa_cert ]; then
|
||||
SSH_DSA_CERT=${SSH_DSA_CERT:-"/data/ssh/ssh_host_dsa_cert"}
|
||||
fi
|
||||
|
||||
if [ -d /etc/ssh ]; then
|
||||
SSH_PORT=${SSH_PORT:-"22"} \
|
||||
SSH_LISTEN_PORT=${SSH_LISTEN_PORT:-"${SSH_PORT}"} \
|
||||
SSH_ED25519_CERT="${SSH_ED25519_CERT:+"HostCertificate "}${SSH_ED25519_CERT}" \
|
||||
SSH_RSA_CERT="${SSH_RSA_CERT:+"HostCertificate "}${SSH_RSA_CERT}" \
|
||||
SSH_ECDSA_CERT="${SSH_ECDSA_CERT:+"HostCertificate "}${SSH_ECDSA_CERT}" \
|
||||
SSH_DSA_CERT="${SSH_DSA_CERT:+"HostCertificate "}${SSH_DSA_CERT}" \
|
||||
SSH_MAX_STARTUPS="${SSH_MAX_STARTUPS:+"MaxStartups "}${SSH_MAX_STARTUPS}" \
|
||||
SSH_MAX_SESSIONS="${SSH_MAX_SESSIONS:+"MaxSessions "}${SSH_MAX_SESSIONS}" \
|
||||
envsubst < /etc/templates/sshd_config > /etc/ssh/sshd_config
|
||||
|
||||
chmod 0644 /etc/ssh/sshd_config
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user