mirror of
https://github.com/go-gitea/gitea.git
synced 2025-08-10 20:41:48 +02:00
Compare commits
17 Commits
v1.17.2
...
v1.14.0-rc
Author | SHA1 | Date | |
---|---|---|---|
|
e99534cfd2 | ||
|
27acf6165e | ||
|
f286a28568 | ||
|
b5c4cb1bde | ||
|
26b98417ad | ||
|
8b0cf88c0c | ||
|
23db3375df | ||
|
14011d77c9 | ||
|
5519e26c2f | ||
|
6feb435867 | ||
|
61444ed8ca | ||
|
d770cc9886 | ||
|
fbaa01998a | ||
|
ac2ae66ae7 | ||
|
ed60fe0986 | ||
|
29e0d62790 | ||
|
7b464fa67b |
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$"]
|
@@ -1,6 +1,3 @@
|
||||
# config for changelog tool
|
||||
# source: https://gitea.com/gitea/changelog
|
||||
|
||||
# The full repository name
|
||||
repo: go-gitea/gitea
|
||||
|
||||
@@ -17,32 +14,28 @@ groups:
|
||||
name: BREAKING
|
||||
labels:
|
||||
- kind/breaking
|
||||
-
|
||||
name: SECURITY
|
||||
labels:
|
||||
- kind/security
|
||||
-
|
||||
name: FEDERATION
|
||||
labels:
|
||||
- theme/federation
|
||||
-
|
||||
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:
|
||||
|
861
.drone.yml
861
.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
|
||||
|
||||
|
120
.eslintrc
120
.eslintrc
@@ -3,27 +3,33 @@ reportUnusedDisableDirectives: true
|
||||
|
||||
ignorePatterns:
|
||||
- /web_src/js/vendor
|
||||
- /templates/base/head.tmpl
|
||||
- /templates/repo/activity.tmpl
|
||||
- /templates/repo/view_file.tmpl
|
||||
|
||||
parserOptions:
|
||||
sourceType: module
|
||||
ecmaVersion: latest
|
||||
ecmaVersion: 2021
|
||||
|
||||
plugins:
|
||||
- eslint-plugin-unicorn
|
||||
- eslint-plugin-import
|
||||
- eslint-plugin-vue
|
||||
- eslint-plugin-html
|
||||
- eslint-plugin-jquery
|
||||
|
||||
extends:
|
||||
- plugin:vue/recommended
|
||||
|
||||
env:
|
||||
es2022: true
|
||||
es2021: true
|
||||
node: true
|
||||
|
||||
globals:
|
||||
__webpack_public_path__: true
|
||||
CodeMirror: false
|
||||
Dropzone: false
|
||||
SimpleMDE: false
|
||||
u2fApi: false
|
||||
|
||||
settings:
|
||||
html/html-extensions: [".tmpl"]
|
||||
@@ -32,6 +38,7 @@ overrides:
|
||||
- files: ["web_src/**/*.js", "web_src/**/*.vue", "templates/**/*.tmpl"]
|
||||
env:
|
||||
browser: true
|
||||
jquery: true
|
||||
node: false
|
||||
- files: ["templates/**/*.tmpl"]
|
||||
rules:
|
||||
@@ -46,12 +53,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]
|
||||
@@ -113,12 +114,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: [2, {ignoreExternal: true}]
|
||||
import/no-default-export: [0]
|
||||
import/no-deprecated: [0]
|
||||
import/no-dynamic-require: [0]
|
||||
import/no-extraneous-dependencies: [2]
|
||||
import/no-import-module-exports: [0]
|
||||
import/no-internal-modules: [0]
|
||||
import/no-mutable-exports: [2]
|
||||
import/no-named-as-default-member: [0]
|
||||
@@ -127,7 +127,6 @@ 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]
|
||||
@@ -141,55 +140,6 @@ rules:
|
||||
import/unambiguous: [0]
|
||||
indent: [2, 2, {SwitchCase: 1}]
|
||||
init-declarations: [0]
|
||||
jquery/no-ajax-events: [2]
|
||||
jquery/no-ajax: [0]
|
||||
jquery/no-animate: [2]
|
||||
jquery/no-attr: [0]
|
||||
jquery/no-bind: [2]
|
||||
jquery/no-class: [0]
|
||||
jquery/no-clone: [2]
|
||||
jquery/no-closest: [0]
|
||||
jquery/no-css: [0]
|
||||
jquery/no-data: [0]
|
||||
jquery/no-deferred: [2]
|
||||
jquery/no-delegate: [2]
|
||||
jquery/no-each: [0]
|
||||
jquery/no-extend: [2]
|
||||
jquery/no-fade: [0]
|
||||
jquery/no-filter: [0]
|
||||
jquery/no-find: [0]
|
||||
jquery/no-global-eval: [2]
|
||||
jquery/no-grep: [2]
|
||||
jquery/no-has: [2]
|
||||
jquery/no-hide: [0]
|
||||
jquery/no-html: [0]
|
||||
jquery/no-in-array: [2]
|
||||
jquery/no-is-array: [2]
|
||||
jquery/no-is-function: [2]
|
||||
jquery/no-is: [0]
|
||||
jquery/no-load: [2]
|
||||
jquery/no-map: [0]
|
||||
jquery/no-merge: [2]
|
||||
jquery/no-param: [2]
|
||||
jquery/no-parent: [0]
|
||||
jquery/no-parents: [0]
|
||||
jquery/no-parse-html: [2]
|
||||
jquery/no-prop: [0]
|
||||
jquery/no-proxy: [2]
|
||||
jquery/no-ready: [0]
|
||||
jquery/no-serialize: [2]
|
||||
jquery/no-show: [0]
|
||||
jquery/no-size: [2]
|
||||
jquery/no-sizzle: [0]
|
||||
jquery/no-slide: [0]
|
||||
jquery/no-submit: [0]
|
||||
jquery/no-text: [0]
|
||||
jquery/no-toggle: [0]
|
||||
jquery/no-trigger: [0]
|
||||
jquery/no-trim: [2]
|
||||
jquery/no-val: [0]
|
||||
jquery/no-when: [2]
|
||||
jquery/no-wrap: [2]
|
||||
key-spacing: [2]
|
||||
keyword-spacing: [2]
|
||||
line-comment-position: [0]
|
||||
@@ -224,7 +174,6 @@ rules:
|
||||
no-confusing-arrow: [0]
|
||||
no-console: [1, {allow: [info, warn, error]}]
|
||||
no-const-assign: [2]
|
||||
no-constant-binary-expression: [2]
|
||||
no-constant-condition: [0]
|
||||
no-constructor-return: [2]
|
||||
no-continue: [0]
|
||||
@@ -330,8 +279,7 @@ 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: ^_, destructuredArrayIgnorePattern: ^_, ignoreRestSiblings: false}]
|
||||
no-unused-vars: [2, {args: all, argsIgnorePattern: ^_, varsIgnorePattern: ^_, caughtErrorsIgnorePattern: ^_, ignoreRestSiblings: false}]
|
||||
no-use-before-define: [2, nofunc]
|
||||
no-useless-backreference: [0]
|
||||
no-useless-call: [2]
|
||||
@@ -363,7 +311,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]
|
||||
@@ -397,7 +344,6 @@ 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]
|
||||
@@ -410,94 +356,52 @@ rules:
|
||||
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-unreadable-iife: [2]
|
||||
unicorn/no-unsafe-regex: [0]
|
||||
unicorn/no-unused-properties: [2]
|
||||
unicorn/no-useless-fallback-in-spread: [2]
|
||||
unicorn/no-useless-length-check: [2]
|
||||
unicorn/no-useless-promise-resolve-reject: [2]
|
||||
unicorn/no-useless-spread: [2]
|
||||
unicorn/no-useless-switch-case: [2]
|
||||
unicorn/no-useless-undefined: [0]
|
||||
unicorn/no-zero-fractions: [2]
|
||||
unicorn/number-literal-case: [0]
|
||||
unicorn/numeric-separators-style: [0]
|
||||
unicorn/prefer-add-event-listener: [2]
|
||||
unicorn/prefer-array-find: [2]
|
||||
unicorn/prefer-array-flat-map: [2]
|
||||
unicorn/prefer-array-flat: [2]
|
||||
unicorn/prefer-array-index-of: [2]
|
||||
unicorn/prefer-array-some: [2]
|
||||
unicorn/prefer-at: [0]
|
||||
unicorn/prefer-code-point: [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-modern-math-apis: [2]
|
||||
unicorn/prefer-module: [2]
|
||||
unicorn/prefer-native-coercion-functions: [2]
|
||||
unicorn/prefer-negative-index: [2]
|
||||
unicorn/prefer-node-append: [0]
|
||||
unicorn/prefer-node-protocol: [0]
|
||||
unicorn/prefer-node-remove: [0]
|
||||
unicorn/prefer-number-properties: [0]
|
||||
unicorn/prefer-object-from-entries: [2]
|
||||
unicorn/prefer-object-has-own: [0]
|
||||
unicorn/prefer-optional-catch-binding: [2]
|
||||
unicorn/prefer-prototype-methods: [0]
|
||||
unicorn/prefer-query-selector: [0]
|
||||
unicorn/prefer-reflect-apply: [0]
|
||||
unicorn/prefer-regexp-test: [2]
|
||||
unicorn/prefer-replace-all: [0]
|
||||
unicorn/prefer-set-has: [0]
|
||||
unicorn/prefer-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/text-encoding-identifier-case: [0]
|
||||
unicorn/throw-new-error: [2]
|
||||
use-isnan: [2]
|
||||
valid-typeof: [2, {requireStringLiterals: true}]
|
||||
|
9
.gitattributes
vendored
9
.gitattributes
vendored
@@ -1,9 +1,6 @@
|
||||
* text=auto eol=lf
|
||||
*.tmpl linguist-language=Handlebars
|
||||
/vendor/** -text -eol linguist-vendored
|
||||
/public/vendor/** -text -eol linguist-vendored
|
||||
/templates/**/*.tmpl linguist-language=Handlebars
|
||||
/.eslintrc linguist-language=YAML
|
||||
/.stylelintrc linguist-language=YAML
|
||||
/public/vendor/** -text -eol linguist-vendored
|
||||
/vendor/** -text -eol linguist-vendored
|
||||
/web_src/fomantic/build/** linguist-generated
|
||||
/web_src/js/vendor/** -text -eol linguist-vendored
|
||||
Dockerfile.* linguist-language=Dockerfile
|
||||
|
94
.github/ISSUE_TEMPLATE/bug-report.yaml
vendored
94
.github/ISSUE_TEMPLATE/bug-report.yaml
vendored
@@ -1,94 +0,0 @@
|
||||
name: Bug Report
|
||||
description: Found something you weren't expecting? Report it here!
|
||||
labels: kind/bug
|
||||
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.
|
||||
6. In particular it's really important to provide pertinent logs. You must give us DEBUG level logs.
|
||||
Please read https://docs.gitea.io/en-us/logging-configuration/#debugging-problems
|
||||
In addition, if your problem relates to git commands set `RUN_MODE=dev` at the top of app.ini
|
||||
- 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 below)
|
||||
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: input
|
||||
id: gitea-ver
|
||||
attributes:
|
||||
label: Gitea Version
|
||||
description: Gitea version (or commit reference) of your instance
|
||||
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: 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: screenshots
|
||||
attributes:
|
||||
label: Screenshots
|
||||
description: If this issue involves the Web Interface, please provide one or more screenshots
|
||||
- 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
|
17
.github/ISSUE_TEMPLATE/config.yml
vendored
17
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,17 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
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.
|
24
.github/ISSUE_TEMPLATE/feature-request.yaml
vendored
24
.github/ISSUE_TEMPLATE/feature-request.yaml
vendored
@@ -1,24 +0,0 @@
|
||||
name: Feature Request
|
||||
description: Got an idea for a feature that Gitea doesn't have currently? Submit your idea here!
|
||||
labels: ["kind/feature", "kind/proposal"]
|
||||
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
|
66
.github/ISSUE_TEMPLATE/ui.bug-report.yaml
vendored
66
.github/ISSUE_TEMPLATE/ui.bug-report.yaml
vendored
@@ -1,66 +0,0 @@
|
||||
name: Web Interface Bug Report
|
||||
description: Something doesn't look quite as it should? Report it here!
|
||||
labels: ["kind/bug", "kind/ui"]
|
||||
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.
|
||||
6. In particular it's really important to provide pertinent logs. If you are certain that this is a javascript
|
||||
error, show us the javascript console. If the error appears to relate to Gitea the server you must also give us
|
||||
DEBUG level logs. (See https://docs.gitea.io/en-us/logging-configuration/#debugging-problems)
|
||||
- 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 below)
|
||||
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
|
||||
- type: input
|
||||
id: gitea-ver
|
||||
attributes:
|
||||
label: Gitea Version
|
||||
description: Gitea version (or commit reference) your instance is running
|
||||
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: 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
|
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.**
|
||||
|
9
.gitignore
vendored
9
.gitignore
vendored
@@ -9,8 +9,6 @@ _test
|
||||
|
||||
# IntelliJ
|
||||
.idea
|
||||
# Goland's output filename can not be set manually
|
||||
/go_build_*
|
||||
|
||||
# MS VSCode
|
||||
.vscode
|
||||
@@ -34,10 +32,7 @@ _testmain.go
|
||||
|
||||
*coverage.out
|
||||
coverage.all
|
||||
cpu.out
|
||||
|
||||
/modules/migration/bindata.go
|
||||
/modules/migration/bindata.go.hash
|
||||
/modules/options/bindata.go
|
||||
/modules/options/bindata.go.hash
|
||||
/modules/public/bindata.go
|
||||
@@ -80,15 +75,11 @@ 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
|
||||
|
@@ -9,72 +9,24 @@ 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
|
||||
- gofumpt
|
||||
- depguard
|
||||
enable-all: false
|
||||
disable-all: true
|
||||
fast: false
|
||||
|
||||
run:
|
||||
go: 1.18
|
||||
timeout: 10m
|
||||
skip-dirs:
|
||||
- node_modules
|
||||
- public
|
||||
- web_src
|
||||
timeout: 3m
|
||||
|
||||
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
|
||||
gofumpt:
|
||||
extra-rules: true
|
||||
lang-version: "1.18"
|
||||
depguard:
|
||||
# TODO: use depguard to replace import checks in gitea-vet
|
||||
list-type: denylist
|
||||
# Check the list against standard lib.
|
||||
include-go-root: true
|
||||
packages-with-error-message:
|
||||
- encoding/json: "use gitea's modules/json instead of encoding/json"
|
||||
- github.com/unknwon/com: "use gitea's util and replacements"
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
@@ -118,6 +70,9 @@ issues:
|
||||
- path: modules/log/
|
||||
linters:
|
||||
- errcheck
|
||||
- path: routers/routes/web.go
|
||||
linters:
|
||||
- dupl
|
||||
- path: routers/api/v1/repo/issue_subscription.go
|
||||
linters:
|
||||
- dupl
|
||||
@@ -155,10 +110,3 @@ issues:
|
||||
- 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
|
||||
|
1
.npmrc
1
.npmrc
@@ -1,5 +1,4 @@
|
||||
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]
|
17
.stylelintrc
17
.stylelintrc
@@ -1,32 +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
|
||||
function-no-unknown: 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
|
||||
shorthand-property-no-redundant-values: true
|
||||
string-quotes: null
|
||||
value-no-vendor-prefix: null
|
||||
|
1634
CHANGELOG.md
1634
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
138
CONTRIBUTING.md
138
CONTRIBUTING.md
@@ -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
|
||||
@@ -134,15 +133,6 @@ Some of the key points:
|
||||
if that is not related to your PR, please make *another* PR for that.
|
||||
* Split big pull requests into multiple small ones. An incremental change
|
||||
will be faster to review than a huge PR.
|
||||
* Use the first comment as a summary explainer of your PR and you should keep this up-to-date as the PR evolves.
|
||||
|
||||
If your PR could cause a breaking change you must add a BREAKING section to this comment e.g.:
|
||||
|
||||
```
|
||||
## :warning: BREAKING :warning:
|
||||
```
|
||||
|
||||
To explain how this could affect users and how to mitigate these changes.
|
||||
|
||||
## Styleguide
|
||||
|
||||
@@ -150,8 +140,8 @@ For imports you should use the following format (_without_ the comments)
|
||||
```go
|
||||
import (
|
||||
// stdlib
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
// local packages
|
||||
"code.gitea.io/gitea/models"
|
||||
@@ -165,7 +155,7 @@ 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).
|
||||
@@ -212,74 +202,9 @@ In general, HTTP methods are chosen as follows:
|
||||
* **PUT** endpoints return status **No Content (204)**, used to **add/assign** existing Objects (e.g. User) to something (e.g. Org-Team)
|
||||
* **PATCH** endpoints return changed object and status **OK (200)**, used to **edit/change** an existing object
|
||||
|
||||
|
||||
An endpoint which changes/edits an object expects all fields to be optional (except ones to identify the object, which are required).
|
||||
### Endpoints returning lists should
|
||||
* support pagination (`page` & `limit` options in query)
|
||||
* set `X-Total-Count` header via **SetTotalCountHeader** ([example](https://github.com/go-gitea/gitea/blob/7aae98cc5d4113f1e9918b7ee7dd09f67c189e3e/routers/api/v1/repo/issue.go#L444))
|
||||
|
||||
## Large Character Comments
|
||||
|
||||
Throughout the codebase there are large-text comments for sections of code, e.g.:
|
||||
|
||||
```go
|
||||
// __________ .__
|
||||
// \______ \ _______ _|__| ______ _ __
|
||||
// | _// __ \ \/ / |/ __ \ \/ \/ /
|
||||
// | | \ ___/\ /| \ ___/\ /
|
||||
// |____|_ /\___ >\_/ |__|\___ >\/\_/
|
||||
// \/ \/ \/
|
||||
```
|
||||
|
||||
These were created using the `figlet` tool with the `graffiti` font.
|
||||
|
||||
A simple way of creating these is to use the following:
|
||||
|
||||
```bash
|
||||
figlet -f graffiti Review | sed -e's+^+// +' - | xclip -sel clip -in
|
||||
```
|
||||
|
||||
## Backports and Frontports
|
||||
|
||||
Occasionally backports of PRs are required.
|
||||
|
||||
The backported PR title should be:
|
||||
|
||||
```
|
||||
Title of backported PR (#ORIGINAL_PR_NUMBER)
|
||||
```
|
||||
|
||||
The first two lines of the summary of the backporting PR should be:
|
||||
|
||||
```
|
||||
Backport #ORIGINAL_PR_NUMBER
|
||||
|
||||
```
|
||||
|
||||
with the rest of the summary matching the original PR. Similarly for frontports
|
||||
|
||||
---
|
||||
|
||||
The below is a script that may be helpful in creating backports. YMMV.
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
PR="$1"
|
||||
SHA="$2"
|
||||
VERSION="$3"
|
||||
|
||||
if [ -z "$SHA" ]; then
|
||||
SHA=$(gh api /repos/go-gitea/gitea/pulls/$PR -q '.merge_commit_sha')
|
||||
fi
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
VERSION="v1.16"
|
||||
fi
|
||||
|
||||
echo git checkout origin/release/"$VERSION" -b backport-$PR-$VERSION
|
||||
git checkout origin/release/"$VERSION" -b backport-$PR-$VERSION
|
||||
git cherry-pick $SHA && git commit --amend && git push zeripath backport-$PR-$VERSION && xdg-open https://github.com/go-gitea/gitea/compare/release/"$VERSION"...zeripath:backport-$PR-$VERSION
|
||||
|
||||
```
|
||||
|
||||
## Developer Certificate of Origin (DCO)
|
||||
|
||||
@@ -292,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.
|
||||
@@ -301,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
|
||||
@@ -337,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/
|
||||
|
||||
@@ -368,11 +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>
|
||||
@@ -400,13 +320,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.
|
||||
@@ -414,26 +334,22 @@ 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 a frontport PR for the changelog to branch `main` and update the version in `docs/config.yaml` to refer to the new version.
|
||||
* 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.
|
||||
* Verify all release assets were correctly published through CI on dl.gitea.io and GitHub releases. Once ACKed:
|
||||
* bump the version of https://dl.gitea.io/gitea/version.json
|
||||
* merge the blog post PR
|
||||
* announce the release in discord `#announcements`
|
||||
|
||||
## Copyright
|
||||
|
||||
Code that you contribute should use the standard copyright header:
|
||||
|
||||
```
|
||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// 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.
|
||||
```
|
||||
|
11
Dockerfile
11
Dockerfile
@@ -1,5 +1,7 @@
|
||||
|
||||
###################################
|
||||
#Build stage
|
||||
FROM golang:1.18-alpine3.16 AS build-env
|
||||
FROM golang:1.16-alpine3.13 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY ${GOPROXY:-direct}
|
||||
@@ -23,7 +25,7 @@ RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
||||
# Begin env-to-ini build
|
||||
RUN go build contrib/environment-to-ini/environment-to-ini.go
|
||||
|
||||
FROM alpine:3.16
|
||||
FROM alpine:3.13
|
||||
LABEL maintainer="maintainers@gitea.io"
|
||||
|
||||
EXPOSE 22 3000
|
||||
@@ -51,7 +53,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
|
||||
@@ -64,5 +66,4 @@ CMD ["/bin/s6-svscan", "/etc/s6"]
|
||||
COPY docker/root /
|
||||
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
||||
COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
||||
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,5 +1,7 @@
|
||||
|
||||
###################################
|
||||
#Build stage
|
||||
FROM golang:1.18-alpine3.16 AS build-env
|
||||
FROM golang:1.16-alpine3.13 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY ${GOPROXY:-direct}
|
||||
@@ -7,7 +9,7 @@ ENV GOPROXY ${GOPROXY:-direct}
|
||||
ARG GITEA_VERSION
|
||||
ARG TAGS="sqlite sqlite_unlock_notify"
|
||||
ENV TAGS "bindata timetzdata $TAGS"
|
||||
ARG CGO_EXTRA_CFLAGS
|
||||
ARG CGO_EXTRA_CFLAGS
|
||||
|
||||
#Build deps
|
||||
RUN apk --no-cache add build-base git nodejs npm
|
||||
@@ -23,7 +25,7 @@ RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \
|
||||
# Begin env-to-ini build
|
||||
RUN go build contrib/environment-to-ini/environment-to-ini.go
|
||||
|
||||
FROM alpine:3.16
|
||||
FROM alpine:3.13
|
||||
LABEL maintainer="maintainers@gitea.io"
|
||||
|
||||
EXPOSE 2222 3000
|
||||
@@ -33,7 +35,6 @@ RUN apk --no-cache add \
|
||||
ca-certificates \
|
||||
gettext \
|
||||
git \
|
||||
curl \
|
||||
gnupg
|
||||
|
||||
RUN addgroup \
|
||||
@@ -45,23 +46,20 @@ RUN addgroup \
|
||||
-s /bin/bash \
|
||||
-u 1000 \
|
||||
-G git \
|
||||
git
|
||||
git && \
|
||||
echo "git:$(dd if=/dev/urandom bs=24 count=1 status=none | base64)" | chpasswd
|
||||
|
||||
RUN mkdir -p /var/lib/gitea /etc/gitea
|
||||
RUN chown git:git /var/lib/gitea /etc/gitea
|
||||
|
||||
COPY docker/rootless /
|
||||
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
||||
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /usr/local/bin/gitea
|
||||
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
|
||||
RUN chmod 755 /usr/local/bin/docker-entrypoint.sh /usr/local/bin/docker-setup.sh /app/gitea/gitea /usr/local/bin/gitea /usr/local/bin/environment-to-ini
|
||||
|
||||
#git:git
|
||||
USER 1000:1000
|
||||
USER git:git
|
||||
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"
|
||||
|
@@ -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)
|
||||
@@ -40,10 +41,3 @@ 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)
|
||||
silentcode <silentcode@senga.org> (@silentcodeg)
|
||||
Wim <wim@42.be> (@42wim)
|
||||
|
335
Makefile
335
Makefile
@@ -24,17 +24,9 @@ SHASUM ?= shasum -a 256
|
||||
HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
|
||||
COMMA := ,
|
||||
|
||||
XGO_VERSION := go-1.18.x
|
||||
|
||||
AIR_PACKAGE ?= github.com/cosmtrek/air@v1.29.0
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.4.0
|
||||
ERRCHECK_PACKAGE ?= github.com/kisielk/errcheck@v1.6.0
|
||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.3.1
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.46.0
|
||||
GXZ_PAGAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.10
|
||||
MISSPELL_PACKAGE ?= github.com/client9/misspell/cmd/misspell@v0.3.4
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.29.0
|
||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||
XGO_VERSION := go-1.16.x
|
||||
MIN_GO_VERSION := 001014000
|
||||
MIN_NODE_VERSION := 010013000
|
||||
|
||||
DOCKER_IMAGE ?= gitea/gitea
|
||||
DOCKER_TAG ?= latest
|
||||
@@ -51,9 +43,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
|
||||
@@ -65,14 +54,15 @@ else
|
||||
SED_INPLACE := sed -i ''
|
||||
endif
|
||||
|
||||
GOFMT ?= gofmt -s
|
||||
|
||||
EXTRA_GOFLAGS ?=
|
||||
|
||||
MAKE_VERSION := $(shell "$(MAKE)" -v | head -n 1)
|
||||
MAKE_VERSION := $(shell $(MAKE) -v | head -n 1)
|
||||
MAKE_EVIDENCE_DIR := .make_evidence
|
||||
|
||||
ifeq ($(RACE_ENABLED),true)
|
||||
GOFLAGS += -race
|
||||
GOTESTFLAGS += -race
|
||||
ifneq ($(RACE_ENABLED),)
|
||||
GOTESTFLAGS ?= -race
|
||||
endif
|
||||
|
||||
STORED_VERSION_FILE := VERSION
|
||||
@@ -84,7 +74,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)
|
||||
@@ -99,9 +89,11 @@ LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(G
|
||||
|
||||
LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64
|
||||
|
||||
GO_PACKAGES ?= $(filter-out code.gitea.io/gitea/models/migrations code.gitea.io/gitea/integrations/migration-test code.gitea.io/gitea/integrations,$(shell $(GO) list ./... | grep -v /vendor/))
|
||||
GO_PACKAGES ?= $(filter-out code.gitea.io/gitea/integrations/migration-test,$(filter-out code.gitea.io/gitea/integrations,$(shell $(GO) list -mod=vendor ./... | grep -v /vendor/)))
|
||||
|
||||
FOMANTIC_WORK_DIR := web_src/fomantic
|
||||
FOMANTIC_CONFIGS := semantic.json web_src/fomantic/theme.config.less web_src/fomantic/_site/globals/site.variables
|
||||
FOMANTIC_DEST := web_src/fomantic/build/semantic.js web_src/fomantic/build/semantic.css
|
||||
FOMANTIC_DEST_DIR := web_src/fomantic/build
|
||||
|
||||
WEBPACK_SOURCES := $(shell find web_src/js web_src/less -type f)
|
||||
WEBPACK_CONFIGS := webpack.config.js
|
||||
@@ -121,9 +113,7 @@ 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 tools
|
||||
|
||||
GO_SOURCES := $(wildcard *.go)
|
||||
GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" -not -path modules/options/bindata.go -not -path modules/public/bindata.go -not -path modules/templates/bindata.go)
|
||||
@@ -132,6 +122,10 @@ ifeq ($(filter $(TAGS_SPLIT),bindata),bindata)
|
||||
GO_SOURCES += $(BINDATA_DEST)
|
||||
endif
|
||||
|
||||
GO_SOURCES_OWN := $(filter-out vendor/% %/bindata.go, $(GO_SOURCES))
|
||||
|
||||
#To update swagger use: GO111MODULE=on go get -u github.com/go-swagger/go-swagger/cmd/swagger
|
||||
SWAGGER := $(GO) run -mod=vendor github.com/go-swagger/go-swagger/cmd/swagger
|
||||
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
|
||||
SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|g
|
||||
SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|"basePath": "/api/v1"|g
|
||||
@@ -171,18 +165,12 @@ help:
|
||||
@echo " - watch-backend watch backend files and continuously rebuild"
|
||||
@echo " - clean delete backend and integration files"
|
||||
@echo " - clean-all delete backend, frontend and integration files"
|
||||
@echo " - deps install dependencies"
|
||||
@echo " - deps-frontend install frontend dependencies"
|
||||
@echo " - deps-backend install backend dependencies"
|
||||
@echo " - lint lint everything"
|
||||
@echo " - lint-frontend lint frontend files"
|
||||
@echo " - lint-backend lint backend files"
|
||||
@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"
|
||||
@@ -194,19 +182,18 @@ help:
|
||||
@echo " - generate-swagger generate the swagger spec from code comments"
|
||||
@echo " - swagger-validate check if the swagger spec is valid"
|
||||
@echo " - golangci-lint run golangci-lint linter"
|
||||
@echo " - revive run revive linter"
|
||||
@echo " - misspell check for misspellings"
|
||||
@echo " - vet examines Go source code and reports suspicious constructs"
|
||||
@echo " - tidy run go mod tidy"
|
||||
@echo " - test[\#TestSpecificName] run unit test"
|
||||
@echo " - test-sqlite[\#TestSpecificName] run integration test for sqlite"
|
||||
@echo " - pr#<index> build and start gitea from a PR with integration test data loaded"
|
||||
|
||||
.PHONY: go-check
|
||||
go-check:
|
||||
$(eval MIN_GO_VERSION_STR := $(shell grep -Eo '^go\s+[0-9]+\.[0-9.]+' go.mod | cut -d' ' -f2))
|
||||
$(eval MIN_GO_VERSION := $(shell printf "%03d%03d%03d" $(shell echo '$(MIN_GO_VERSION_STR)' | tr '.' ' ')))
|
||||
$(eval GO_VERSION := $(shell printf "%03d%03d%03d" $(shell $(GO) version | grep -Eo '[0-9]+\.[0-9.]+' | tr '.' ' ');))
|
||||
@if [ "$(GO_VERSION)" -lt "$(MIN_GO_VERSION)" ]; then \
|
||||
echo "Gitea requires Go $(MIN_GO_VERSION_STR) or greater to build. You can get it at https://go.dev/dl/"; \
|
||||
echo "Gitea requires Go 1.14 or greater to build. You can get it at https://golang.org/dl/"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
@@ -219,18 +206,16 @@ git-check:
|
||||
|
||||
.PHONY: node-check
|
||||
node-check:
|
||||
$(eval MIN_NODE_VERSION_STR := $(shell grep -Eo '"node":.*[0-9.]+"' package.json | sed -n 's/.*[^0-9.]\([0-9.]*\)"/\1/p'))
|
||||
$(eval MIN_NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell echo '$(MIN_NODE_VERSION_STR)' | tr '.' ' ')))
|
||||
$(eval NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v | cut -c2- | tr '.' ' ');))
|
||||
$(eval NPM_MISSING := $(shell hash npm > /dev/null 2>&1 || echo 1))
|
||||
@if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" -o "$(NPM_MISSING)" = "1" ]; then \
|
||||
echo "Gitea requires Node.js $(MIN_NODE_VERSION_STR) or greater and npm to build. You can get it at https://nodejs.org/en/download/"; \
|
||||
echo "Gitea requires Node.js 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)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@@ -243,13 +228,14 @@ clean:
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
@echo "Running gitea-fmt (with gofumpt)..."
|
||||
@MISSPELL_PACKAGE=$(MISSPELL_PACKAGE) GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
|
||||
@echo "Running go fmt..."
|
||||
@$(GOFMT) -w $(GO_SOURCES_OWN)
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
@echo "Running go vet..."
|
||||
@GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet
|
||||
@$(GO) vet $(GO_PACKAGES)
|
||||
@GOOS= GOARCH= $(GO) build -mod=vendor code.gitea.io/gitea-vet
|
||||
@$(GO) vet -vettool=gitea-vet $(GO_PACKAGES)
|
||||
|
||||
.PHONY: $(TAGS_EVIDENCE)
|
||||
@@ -263,7 +249,7 @@ endif
|
||||
|
||||
.PHONY: generate-swagger
|
||||
generate-swagger:
|
||||
$(GO) run $(SWAGGER_PACKAGE) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
|
||||
$(SWAGGER) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
|
||||
|
||||
@@ -279,18 +265,41 @@ swagger-check: generate-swagger
|
||||
.PHONY: swagger-validate
|
||||
swagger-validate:
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)'
|
||||
$(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)'
|
||||
$(SWAGGER) validate './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||
|
||||
.PHONY: errcheck
|
||||
errcheck:
|
||||
@hash errcheck > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u github.com/kisielk/errcheck; \
|
||||
fi
|
||||
@echo "Running errcheck..."
|
||||
$(GO) run $(ERRCHECK_PACKAGE) $(GO_PACKAGES)
|
||||
@errcheck $(GO_PACKAGES)
|
||||
|
||||
.PHONY: revive
|
||||
revive:
|
||||
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
|
||||
@echo "Running misspell-check..."
|
||||
@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
|
||||
@echo "Running go misspell..."
|
||||
@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=$$(MISSPELL_PACKAGE=$(MISSPELL_PACKAGE) GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -l '{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}"; \
|
||||
@@ -301,21 +310,21 @@ fmt-check:
|
||||
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 eslint --color --max-warnings=0 web_src/js build templates webpack.config.js
|
||||
npx stylelint --color --max-warnings=0 web_src/less
|
||||
|
||||
.PHONY: lint-backend
|
||||
lint-backend: golangci-lint vet editorconfig-checker
|
||||
lint-backend: golangci-lint revive vet
|
||||
|
||||
.PHONY: watch
|
||||
watch:
|
||||
@@ -328,26 +337,22 @@ watch-frontend: node-check node_modules
|
||||
|
||||
.PHONY: watch-backend
|
||||
watch-backend: go-check
|
||||
$(GO) run $(AIR_PACKAGE) -c .air.toml
|
||||
@hash air > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u github.com/cosmtrek/air; \
|
||||
fi
|
||||
air -c .air.conf
|
||||
|
||||
.PHONY: test
|
||||
test: test-frontend test-backend
|
||||
|
||||
.PHONY: test-backend
|
||||
test-backend:
|
||||
@echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
|
||||
@$(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_PACKAGES)
|
||||
|
||||
.PHONY: test-frontend
|
||||
test-frontend: node_modules
|
||||
@NODE_OPTIONS="--experimental-vm-modules --no-warnings" npx jest --color
|
||||
test:
|
||||
@echo "Running go test with -tags '$(TEST_TAGS)'..."
|
||||
@$(GO) test $(GOTESTFLAGS) -mod=vendor -tags='$(TEST_TAGS)' $(GO_PACKAGES)
|
||||
|
||||
.PHONY: test-check
|
||||
test-check:
|
||||
@echo "Running test-check...";
|
||||
@diff=$$(git status -s); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "make test-backend has changed files in the source tree:"; \
|
||||
echo "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"; \
|
||||
@@ -357,33 +362,26 @@ test-check:
|
||||
.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='$(TEST_TAGS)' -run $(subst .,/,$*) $(GO_PACKAGES)
|
||||
|
||||
.PHONY: coverage
|
||||
coverage:
|
||||
grep '^\(mode: .*\)\|\(.*:[0-9]\+\.[0-9]\+,[0-9]\+\.[0-9]\+ [0-9]\+ [0-9]\+\)$$' coverage.out > coverage-bodged.out
|
||||
grep '^\(mode: .*\)\|\(.*:[0-9]\+\.[0-9]\+,[0-9]\+\.[0-9]\+ [0-9]\+ [0-9]\+\)$$' integration.coverage.out > integration.coverage-bodged.out
|
||||
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
|
||||
|
||||
.PHONY: tidy
|
||||
tidy:
|
||||
$(eval MIN_GO_VERSION := $(shell grep -Eo '^go\s+[0-9]+\.[0-9.]+' go.mod | cut -d' ' -f2))
|
||||
$(GO) mod tidy -compat=$(MIN_GO_VERSION)
|
||||
@echo "Running unit-test-coverage -tags '$(TEST_TAGS)'..."
|
||||
@$(GO) test $(GOTESTFLAGS) -mod=vendor -tags='$(TEST_TAGS)' -cover -coverprofile coverage.out $(GO_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
|
||||
|
||||
.PHONY: vendor
|
||||
vendor: tidy
|
||||
$(GO) mod vendor
|
||||
vendor:
|
||||
$(GO) mod tidy && $(GO) mod vendor
|
||||
|
||||
.PHONY: gomod-check
|
||||
gomod-check: tidy
|
||||
@diff=$$(git diff go.sum); \
|
||||
.PHONY: test-vendor
|
||||
test-vendor: vendor
|
||||
@diff=$$(git diff vendor/); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make tidy' and commit the result:"; \
|
||||
echo "Please run 'make vendor' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@@ -401,14 +399,8 @@ test-sqlite\#%: integrations.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./integrations.sqlite.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-sqlite-migration
|
||||
test-sqlite-migration: migrations.sqlite.test migrations.individual.sqlite.test generate-ini-sqlite
|
||||
test-sqlite-migration: migrations.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./migrations.sqlite.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./migrations.individual.sqlite.test
|
||||
|
||||
.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.individual.sqlite.test -test.run $(subst .,/,$*)
|
||||
|
||||
|
||||
generate-ini-mysql:
|
||||
sed -e 's|{{TEST_MYSQL_HOST}}|${TEST_MYSQL_HOST}|g' \
|
||||
@@ -427,9 +419,8 @@ test-mysql\#%: integrations.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./integrations.mysql.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-mysql-migration
|
||||
test-mysql-migration: migrations.mysql.test migrations.individual.mysql.test generate-ini-mysql
|
||||
test-mysql-migration: migrations.mysql.test generate-ini-mysql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./migrations.mysql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql.ini ./migrations.individual.mysql.test
|
||||
|
||||
generate-ini-mysql8:
|
||||
sed -e 's|{{TEST_MYSQL8_HOST}}|${TEST_MYSQL8_HOST}|g' \
|
||||
@@ -448,9 +439,8 @@ test-mysql8\#%: integrations.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./integrations.mysql8.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-mysql8-migration
|
||||
test-mysql8-migration: migrations.mysql8.test migrations.individual.mysql8.test generate-ini-mysql8
|
||||
test-mysql8-migration: migrations.mysql8.test generate-ini-mysql8
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./migrations.mysql8.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mysql8.ini ./migrations.individual.mysql8.test
|
||||
|
||||
generate-ini-pgsql:
|
||||
sed -e 's|{{TEST_PGSQL_HOST}}|${TEST_PGSQL_HOST}|g' \
|
||||
@@ -470,9 +460,8 @@ test-pgsql\#%: integrations.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./integrations.pgsql.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-pgsql-migration
|
||||
test-pgsql-migration: migrations.pgsql.test migrations.individual.pgsql.test generate-ini-pgsql
|
||||
test-pgsql-migration: migrations.pgsql.test generate-ini-pgsql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./migrations.pgsql.test
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/pgsql.ini ./migrations.individual.pgsql.test
|
||||
|
||||
generate-ini-mssql:
|
||||
sed -e 's|{{TEST_MSSQL_HOST}}|${TEST_MSSQL_HOST}|g' \
|
||||
@@ -491,9 +480,8 @@ test-mssql\#%: integrations.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./integrations.mssql.test -test.run $(subst .,/,$*)
|
||||
|
||||
.PHONY: test-mssql-migration
|
||||
test-mssql-migration: migrations.mssql.test migrations.individual.mssql.test generate-ini-mssql
|
||||
test-mssql-migration: migrations.mssql.test generate-ini-mssql
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./migrations.mssql.test -test.failfast
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/mssql.ini ./migrations.individual.mssql.test -test.failfast
|
||||
|
||||
.PHONY: bench-sqlite
|
||||
bench-sqlite: integrations.sqlite.test generate-ini-sqlite
|
||||
@@ -515,30 +503,23 @@ bench-pgsql: integrations.pgsql.test generate-ini-pgsql
|
||||
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
|
||||
|
||||
.PHONY: integration-test-coverage-sqlite
|
||||
integration-test-coverage-sqlite: integrations.cover.sqlite.test generate-ini-sqlite
|
||||
GITEA_ROOT="$(CURDIR)" GITEA_CONF=integrations/sqlite.ini ./integrations.cover.sqlite.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 '$(TEST_TAGS)'
|
||||
|
||||
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
|
||||
|
||||
integrations.cover.sqlite.test: git-check $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations -coverpkg $(shell echo $(GO_PACKAGES) | tr ' ' ',') -o integrations.cover.sqlite.test -tags '$(TEST_TAGS)'
|
||||
$(GO) test $(GOTESTFLAGS) -mod=vendor -c code.gitea.io/gitea/integrations -coverpkg $(shell echo $(GO_PACKAGES) | tr ' ' ',') -o integrations.cover.test
|
||||
|
||||
.PHONY: migrations.mysql.test
|
||||
migrations.mysql.test: $(GO_SOURCES)
|
||||
@@ -560,26 +541,6 @@ migrations.mssql.test: $(GO_SOURCES)
|
||||
migrations.sqlite.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/integrations/migration-test -o migrations.sqlite.test -tags '$(TEST_TAGS)'
|
||||
|
||||
.PHONY: migrations.individual.mysql.test
|
||||
migrations.individual.mysql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/models/migrations -o migrations.individual.mysql.test
|
||||
|
||||
.PHONY: migrations.individual.mysql8.test
|
||||
migrations.individual.mysql8.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/models/migrations -o migrations.individual.mysql8.test
|
||||
|
||||
.PHONY: migrations.individual.pgsql.test
|
||||
migrations.individual.pgsql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/models/migrations -o migrations.individual.pgsql.test
|
||||
|
||||
.PHONY: migrations.individual.mssql.test
|
||||
migrations.individual.mssql.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/models/migrations -o migrations.individual.mssql.test
|
||||
|
||||
.PHONY: migrations.individual.sqlite.test
|
||||
migrations.individual.sqlite.test: $(GO_SOURCES)
|
||||
$(GO) test $(GOTESTFLAGS) -c code.gitea.io/gitea/models/migrations -o migrations.individual.sqlite.test -tags '$(TEST_TAGS)'
|
||||
|
||||
.PHONY: check
|
||||
check: test
|
||||
|
||||
@@ -591,7 +552,7 @@ install: $(wildcard *.go)
|
||||
build: frontend backend
|
||||
|
||||
.PHONY: frontend
|
||||
frontend: $(WEBPACK_DEST)
|
||||
frontend: node-check $(WEBPACK_DEST)
|
||||
|
||||
.PHONY: backend
|
||||
backend: go-check generate $(EXECUTABLE)
|
||||
@@ -599,44 +560,50 @@ 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)
|
||||
|
||||
.PHONY: release-windows
|
||||
release-windows: | $(DIST_DIRS)
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||
ifeq (,$(findstring gogit,$(TAGS)))
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo gogit $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
|
||||
endif
|
||||
ifeq ($(CI),true)
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
||||
fi
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -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
|
||||
|
||||
.PHONY: release-linux
|
||||
release-linux: | $(DIST_DIRS)
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),true)
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
||||
fi
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
|
||||
.PHONY: release-darwin
|
||||
release-darwin: | $(DIST_DIRS)
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),true)
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
||||
fi
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
|
||||
.PHONY: release-copy
|
||||
release-copy: | $(DIST_DIRS)
|
||||
cd $(DIST); for file in `find . -type f -name "*"`; do cp $${file} ./release/; done;
|
||||
cd $(DIST); for file in `find /build -type f -name "*"`; do cp $${file} ./release/; done;
|
||||
|
||||
.PHONY: release-check
|
||||
release-check: | $(DIST_DIRS)
|
||||
@@ -644,16 +611,15 @@ release-check: | $(DIST_DIRS)
|
||||
|
||||
.PHONY: release-compress
|
||||
release-compress: | $(DIST_DIRS)
|
||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && $(GO) run $(GXZ_PAGAGE) -k -9 $${file}; done;
|
||||
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
GO111MODULE=off $(GO) get -u github.com/ulikunitz/xz/cmd/gxz; \
|
||||
fi
|
||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
|
||||
|
||||
.PHONY: release-sources
|
||||
release-sources: | $(DIST_DIRS)
|
||||
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 "^")./)
|
||||
# use transform to a add a release-folder prefix; in bsdtar the transform parameter equivalent is -s
|
||||
$(eval TRANSFORM := $(shell tar --help | grep -q bsdtar && echo "-s '/^./gitea-src-$(VERSION)/'" || echo "--transform 's|^./|gitea-src-$(VERSION)/|'"))
|
||||
tar $(addprefix $(EXCL),$(TAR_EXCLUDES)) $(TRANSFORM) -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
|
||||
@@ -667,25 +633,6 @@ docs:
|
||||
fi
|
||||
cd docs; make trans-copy clean build-offline;
|
||||
|
||||
.PHONY: deps
|
||||
deps: deps-frontend deps-backend
|
||||
|
||||
.PHONY: deps-frontend
|
||||
deps-frontend: node_modules
|
||||
|
||||
.PHONY: deps-backend
|
||||
deps-backend:
|
||||
$(GO) mod download
|
||||
$(GO) install $(AIR_PACKAGE)
|
||||
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE)
|
||||
$(GO) install $(ERRCHECK_PACKAGE)
|
||||
$(GO) install $(GOFUMPT_PACKAGE)
|
||||
$(GO) install $(GOLANGCI_LINT_PACKAGE)
|
||||
$(GO) install $(GXZ_PAGAGE)
|
||||
$(GO) install $(MISSPELL_PACKAGE)
|
||||
$(GO) install $(SWAGGER_PACKAGE)
|
||||
$(GO) install $(XGO_PACKAGE)
|
||||
|
||||
node_modules: package-lock.json
|
||||
npm install --no-save
|
||||
@touch node_modules
|
||||
@@ -698,20 +645,22 @@ 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/
|
||||
cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build
|
||||
$(SED_INPLACE) -e 's/\r//g' $(FOMANTIC_WORK_DIR)/build/semantic.css $(FOMANTIC_WORK_DIR)/build/semantic.js
|
||||
rm -f $(FOMANTIC_WORK_DIR)/build/*.min.*
|
||||
fomantic: $(FOMANTIC_DEST)
|
||||
|
||||
$(FOMANTIC_DEST): $(FOMANTIC_CONFIGS) | node_modules
|
||||
@if [ ! -d node_modules/fomantic-ui ]; then \
|
||||
npm install --no-save --no-package-lock fomantic-ui@2.8.7; \
|
||||
fi
|
||||
rm -rf $(FOMANTIC_DEST_DIR)
|
||||
cp -f web_src/fomantic/theme.config.less node_modules/fomantic-ui/src/theme.config
|
||||
cp -rf 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
|
||||
@touch $(WEBPACK_DEST)
|
||||
@@ -731,17 +680,6 @@ svg-check: svg
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: lockfile-check
|
||||
lockfile-check:
|
||||
npm install --package-lock-only
|
||||
@diff=$$(git diff package-lock.json); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "package-lock.json is inconsistent with package.json"; \
|
||||
echo "Please run 'npm install --package-lock-only' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: update-translations
|
||||
update-translations:
|
||||
mkdir -p ./translations
|
||||
@@ -761,8 +699,8 @@ 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@5 imagemin-zopfli@7
|
||||
generate-images:
|
||||
npm install --no-save --no-package-lock fabric imagemin-zopfli
|
||||
node build/generate-images.js $(TAGS)
|
||||
|
||||
.PHONY: generate-manpage
|
||||
@@ -779,18 +717,11 @@ pr\#%: clean-all
|
||||
|
||||
.PHONY: golangci-lint
|
||||
golangci-lint:
|
||||
$(GO) run $(GOLANGCI_LINT_PACKAGE) run
|
||||
|
||||
# workaround step for the lint-backend-windows CI task because 'go run' can not
|
||||
# have distinct GOOS/GOARCH for its build and run steps
|
||||
.PHONY: golangci-lint-windows
|
||||
golangci-lint-windows:
|
||||
@GOOS= GOARCH= $(GO) install $(GOLANGCI_LINT_PACKAGE)
|
||||
golangci-lint run
|
||||
|
||||
.PHONY: editorconfig-checker
|
||||
editorconfig-checker:
|
||||
$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) templates
|
||||
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
export BINARY="golangci-lint"; \
|
||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.37.0; \
|
||||
fi
|
||||
golangci-lint run --timeout 10m
|
||||
|
||||
.PHONY: docker
|
||||
docker:
|
||||
|
41
README.md
41
README.md
@@ -1,21 +1,24 @@
|
||||
<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"/>
|
||||
<img alt="Gitea" src="https://raw.githubusercontent.com/go-gitea/gitea/master/public/img/gitea.svg" width="220"/>
|
||||
</a>
|
||||
</p>
|
||||
<h1 align="center">Gitea - Git with a cup of tea</h1>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://drone.gitea.io/go-gitea/gitea" title="Build Status">
|
||||
<img src="https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg?ref=refs/heads/main">
|
||||
<img src="https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg?ref=refs/heads/master">
|
||||
</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 href="https://microbadger.com/images/gitea/gitea" title="Get your own image badge on microbadger.com">
|
||||
<img src="https://images.microbadger.com/badges/image/gitea/gitea.svg">
|
||||
</a>
|
||||
<a href="https://goreportcard.com/report/code.gitea.io/gitea" title="Go Report Card">
|
||||
<a href="https://codecov.io/gh/go-gitea/gitea" title="Codecov">
|
||||
<img src="https://codecov.io/gh/go-gitea/gitea/branch/master/graph/badge.svg">
|
||||
</a>
|
||||
<a href="https://godoc.org/code.gitea.io/gitea" title="Go Report Card">
|
||||
<img src="https://goreportcard.com/badge/code.gitea.io/gitea">
|
||||
</a>
|
||||
<a href="https://godoc.org/code.gitea.io/gitea" title="GoDoc">
|
||||
@@ -36,8 +39,8 @@
|
||||
<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 href="https://www.tickgit.com/browse?repo=github.com/go-gitea/gitea" title="TODOs">
|
||||
<img src="https://badgen.net/https/api.tickgit.com/badgen/github.com/go-gitea/gitea">
|
||||
</a>
|
||||
<a href="https://www.bountysource.com/teams/gitea" title="Bountysource">
|
||||
<img src="https://img.shields.io/bountysource/team/gitea/activity">
|
||||
@@ -67,18 +70,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 Stable](https://go.dev/dl/), required version is defined in [go.mod](/go.mod).
|
||||
- `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,16 +103,6 @@ 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/).
|
||||
@@ -157,7 +152,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
|
||||
|
24
README_ZH.md
24
README_ZH.md
@@ -1,21 +1,24 @@
|
||||
<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"/>
|
||||
<img alt="Gitea" src="https://raw.githubusercontent.com/go-gitea/gitea/master/public/img/gitea.svg" width="220"/>
|
||||
</a>
|
||||
</p>
|
||||
<h1 align="center">Gitea - Git with a cup of tea</h1>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://drone.gitea.io/go-gitea/gitea" title="Build Status">
|
||||
<img src="https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg?ref=refs/heads/main">
|
||||
<img src="https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg?ref=refs/heads/master">
|
||||
</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 href="https://microbadger.com/images/gitea/gitea" title="Get your own image badge on microbadger.com">
|
||||
<img src="https://images.microbadger.com/badges/image/gitea/gitea.svg">
|
||||
</a>
|
||||
<a href="https://goreportcard.com/report/code.gitea.io/gitea" title="Go Report Card">
|
||||
<a href="https://codecov.io/gh/go-gitea/gitea" title="Codecov">
|
||||
<img src="https://codecov.io/gh/go-gitea/gitea/branch/master/graph/badge.svg">
|
||||
</a>
|
||||
<a href="https://godoc.org/code.gitea.io/gitea" title="Go Report Card">
|
||||
<img src="https://goreportcard.com/badge/code.gitea.io/gitea">
|
||||
</a>
|
||||
<a href="https://godoc.org/code.gitea.io/gitea" title="GoDoc">
|
||||
@@ -36,8 +39,8 @@
|
||||
<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 href="https://www.tickgit.com/browse?repo=github.com/go-gitea/gitea" title="TODOs">
|
||||
<img src="https://badgen.net/https/api.tickgit.com/badgen/github.com/go-gitea/gitea">
|
||||
</a>
|
||||
<a href="https://img.shields.io/bountysource/team/gitea" title="Bountysource">
|
||||
<img src="https://img.shields.io/bountysource/team/gitea/activity">
|
||||
@@ -68,11 +71,6 @@ Gitea 的首要目标是创建一个极易安装,运行非常快速,安装
|
||||
|
||||
Fork -> Patch -> Push -> Pull Request
|
||||
|
||||
## 翻译
|
||||
|
||||
多语言翻译是基于Crowdin进行的.
|
||||
[](https://crowdin.com/project/gitea)
|
||||
|
||||
## 作者
|
||||
|
||||
* [Maintainers](https://github.com/orgs/go-gitea/people)
|
||||
@@ -81,7 +79,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) 文件中。
|
||||
|
||||
## 截图
|
||||
|
||||
|
@@ -1,31 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" id="main_outline" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 640 640" style="enable-background:new 0 0 640 640;" xml:space="preserve">
|
||||
<g>
|
||||
<path id="teabag" style="fill:#FFFFFF" d="M395.9,484.2l-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5,21.2-17.9,33.8-11.8
|
||||
c17.2,8.3,27.1,13,27.1,13l-0.1-109.2l16.7-0.1l0.1,117.1c0,0,57.4,24.2,83.1,40.1c3.7,2.3,10.2,6.8,12.9,14.4
|
||||
c2.1,6.1,2,13.1-1,19.3l-61,126.9C423.6,484.9,408.4,490.3,395.9,484.2z"/>
|
||||
<g>
|
||||
<g>
|
||||
<path style="fill:#609926" d="M622.7,149.8c-4.1-4.1-9.6-4-9.6-4s-117.2,6.6-177.9,8c-13.3,0.3-26.5,0.6-39.6,0.7c0,39.1,0,78.2,0,117.2
|
||||
c-5.5-2.6-11.1-5.3-16.6-7.9c0-36.4-0.1-109.2-0.1-109.2c-29,0.4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5
|
||||
c-9.8-0.6-22.5-2.1-39,1.5c-8.7,1.8-33.5,7.4-53.8,26.9C-4.9,212.4,6.6,276.2,8,285.8c1.7,11.7,6.9,44.2,31.7,72.5
|
||||
c45.8,56.1,144.4,54.8,144.4,54.8s12.1,28.9,30.6,55.5c25,33.1,50.7,58.9,75.7,62c63,0,188.9-0.1,188.9-0.1s12,0.1,28.3-10.3
|
||||
c14-8.5,26.5-23.4,26.5-23.4s12.9-13.8,30.9-45.3c5.5-9.7,10.1-19.1,14.1-28c0,0,55.2-117.1,55.2-231.1
|
||||
C633.2,157.9,624.7,151.8,622.7,149.8z M125.6,353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6,321.8,60,295.4
|
||||
c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5,38.5-30c13.8-3.7,31-3.1,31-3.1s7.1,59.4,15.7,94.2c7.2,29.2,24.8,77.7,24.8,77.7
|
||||
S142.5,359.9,125.6,353.9z M425.9,461.5c0,0-6.1,14.5-19.6,15.4c-5.8,0.4-10.3-1.2-10.3-1.2s-0.3-0.1-5.3-2.1l-112.9-55
|
||||
c0,0-10.9-5.7-12.8-15.6c-2.2-8.1,2.7-18.1,2.7-18.1L322,273c0,0,4.8-9.7,12.2-13c0.6-0.3,2.3-1,4.5-1.5c8.1-2.1,18,2.8,18,2.8
|
||||
l110.7,53.7c0,0,12.6,5.7,15.3,16.2c1.9,7.4-0.5,14-1.8,17.2C474.6,363.8,425.9,461.5,425.9,461.5z"/>
|
||||
<path style="fill:#609926" d="M326.8,380.1c-8.2,0.1-15.4,5.8-17.3,13.8c-1.9,8,2,16.3,9.1,20c7.7,4,17.5,1.8,22.7-5.4
|
||||
c5.1-7.1,4.3-16.9-1.8-23.1l24-49.1c1.5,0.1,3.7,0.2,6.2-0.5c4.1-0.9,7.1-3.6,7.1-3.6c4.2,1.8,8.6,3.8,13.2,6.1
|
||||
c4.8,2.4,9.3,4.9,13.4,7.3c0.9,0.5,1.8,1.1,2.8,1.9c1.6,1.3,3.4,3.1,4.7,5.5c1.9,5.5-1.9,14.9-1.9,14.9
|
||||
c-2.3,7.6-18.4,40.6-18.4,40.6c-8.1-0.2-15.3,5-17.7,12.5c-2.6,8.1,1.1,17.3,8.9,21.3c7.8,4,17.4,1.7,22.5-5.3
|
||||
c5-6.8,4.6-16.3-1.1-22.6c1.9-3.7,3.7-7.4,5.6-11.3c5-10.4,13.5-30.4,13.5-30.4c0.9-1.7,5.7-10.3,2.7-21.3
|
||||
c-2.5-11.4-12.6-16.7-12.6-16.7c-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3c4.7-9.7,9.4-19.3,14.1-29
|
||||
c-4.1-2-8.1-4-12.2-6.1c-4.8,9.8-9.7,19.7-14.5,29.5c-6.7-0.1-12.9,3.5-16.1,9.4c-3.4,6.3-2.7,14.1,1.9,19.8
|
||||
C343.2,346.5,335,363.3,326.8,380.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.5 KiB |
10
build.go
10
build.go
@@ -2,7 +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
|
||||
|
||||
package main
|
||||
|
||||
@@ -10,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
|
||||
|
||||
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: append([]string{cmd}, args...),
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
}
|
||||
return c.Run()
|
||||
}
|
||||
|
||||
type fileCollector struct {
|
||||
dirs []string
|
||||
includePatterns []*regexp.Regexp
|
||||
excludePatterns []*regexp.Regexp
|
||||
batchSize int
|
||||
}
|
||||
|
||||
func newFileCollector(fileFilter string, batchSize int) (*fileCollector, error) {
|
||||
co := &fileCollector{batchSize: batchSize}
|
||||
if fileFilter == "go-own" {
|
||||
co.dirs = []string{
|
||||
"build",
|
||||
"cmd",
|
||||
"contrib",
|
||||
"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, 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, hasChangedFiles, doWriteFile bool) error {
|
||||
for _, file := range files {
|
||||
if err := codeformat.FormatGoImports(file, hasChangedFiles, doWriteFile); err != nil {
|
||||
log.Printf("failed to format go imports: %s, err=%v", file, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
mainOptions, subCmd, subArgs := parseArgs()
|
||||
if subCmd == "" {
|
||||
showUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
optionLogVerbose = mainOptions["verbose"] != ""
|
||||
|
||||
fc, err := newFileCollectorFromMainOptions(mainOptions)
|
||||
if err != nil {
|
||||
log.Fatalf("can not create file collector: %s", err.Error())
|
||||
}
|
||||
|
||||
fileBatches, err := fc.collectFiles()
|
||||
if err != nil {
|
||||
log.Fatalf("can not collect files: %s", err.Error())
|
||||
}
|
||||
|
||||
processed := 0
|
||||
var cmdErrors []error
|
||||
for _, files := range fileBatches {
|
||||
if len(files) == 0 {
|
||||
break
|
||||
}
|
||||
substArgs := substArgFiles(subArgs, files)
|
||||
logVerbose("batch cmd: %s %v", subCmd, substArgs)
|
||||
switch subCmd {
|
||||
case "gitea-fmt":
|
||||
if containsString(subArgs, "-d") {
|
||||
log.Print("the -d option is not supported by gitea-fmt")
|
||||
}
|
||||
cmdErrors = append(cmdErrors, giteaFormatGoImports(files, containsString(subArgs, "-l"), containsString(subArgs, "-w")))
|
||||
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra", "-lang", "1.17"}, substArgs...)))
|
||||
case "misspell":
|
||||
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("MISSPELL_PACKAGE")}, substArgs...)))
|
||||
default:
|
||||
log.Fatalf("unknown cmd: %s %v", subCmd, subArgs)
|
||||
}
|
||||
processed += len(files)
|
||||
}
|
||||
|
||||
logVerbose("processed %d files", processed)
|
||||
exitWithCmdErrors(subCmd, subArgs, cmdErrors)
|
||||
}
|
@@ -1,201 +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"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var importPackageGroupOrders = map[string]int{
|
||||
"": 1, // internal
|
||||
"code.gitea.io/gitea/": 2,
|
||||
}
|
||||
|
||||
var errInvalidCommentBetweenImports = errors.New("comments between imported packages are invalid, please move comments to the end of the package line")
|
||||
|
||||
var (
|
||||
importBlockBegin = []byte("\nimport (\n")
|
||||
importBlockEnd = []byte("\n)")
|
||||
)
|
||||
|
||||
type importLineParsed struct {
|
||||
group string
|
||||
pkg string
|
||||
content string
|
||||
}
|
||||
|
||||
func parseImportLine(line string) (*importLineParsed, error) {
|
||||
il := &importLineParsed{content: line}
|
||||
p1 := strings.IndexRune(line, '"')
|
||||
if p1 == -1 {
|
||||
return nil, errors.New("invalid import line: " + line)
|
||||
}
|
||||
p1++
|
||||
p := strings.IndexRune(line[p1:], '"')
|
||||
if p == -1 {
|
||||
return nil, errors.New("invalid import line: " + line)
|
||||
}
|
||||
p2 := p1 + p
|
||||
il.pkg = line[p1:p2]
|
||||
|
||||
pDot := strings.IndexRune(il.pkg, '.')
|
||||
pSlash := strings.IndexRune(il.pkg, '/')
|
||||
if pDot != -1 && pDot < pSlash {
|
||||
il.group = "domain-package"
|
||||
}
|
||||
for groupName := range importPackageGroupOrders {
|
||||
if groupName == "" {
|
||||
continue // skip internal
|
||||
}
|
||||
if strings.HasPrefix(il.pkg, groupName) {
|
||||
il.group = groupName
|
||||
}
|
||||
}
|
||||
return il, nil
|
||||
}
|
||||
|
||||
type (
|
||||
importLineGroup []*importLineParsed
|
||||
importLineGroupMap map[string]importLineGroup
|
||||
)
|
||||
|
||||
func formatGoImports(contentBytes []byte) ([]byte, error) {
|
||||
p1 := bytes.Index(contentBytes, importBlockBegin)
|
||||
if p1 == -1 {
|
||||
return nil, nil
|
||||
}
|
||||
p1 += len(importBlockBegin)
|
||||
p := bytes.Index(contentBytes[p1:], importBlockEnd)
|
||||
if p == -1 {
|
||||
return nil, nil
|
||||
}
|
||||
p2 := p1 + p
|
||||
|
||||
importGroups := importLineGroupMap{}
|
||||
r := bytes.NewBuffer(contentBytes[p1:p2])
|
||||
eof := false
|
||||
for !eof {
|
||||
line, err := r.ReadString('\n')
|
||||
eof = err == io.EOF
|
||||
if err != nil && !eof {
|
||||
return nil, err
|
||||
}
|
||||
line = strings.TrimSpace(line)
|
||||
if line != "" {
|
||||
if strings.HasPrefix(line, "//") || strings.HasPrefix(line, "/*") {
|
||||
return nil, errInvalidCommentBetweenImports
|
||||
}
|
||||
importLine, err := parseImportLine(line)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
importGroups[importLine.group] = append(importGroups[importLine.group], importLine)
|
||||
}
|
||||
}
|
||||
|
||||
var groupNames []string
|
||||
for groupName, importLines := range importGroups {
|
||||
groupNames = append(groupNames, groupName)
|
||||
sort.Slice(importLines, func(i, j int) bool {
|
||||
return strings.Compare(importLines[i].pkg, importLines[j].pkg) < 0
|
||||
})
|
||||
}
|
||||
|
||||
sort.Slice(groupNames, func(i, j int) bool {
|
||||
n1 := groupNames[i]
|
||||
n2 := groupNames[j]
|
||||
o1 := importPackageGroupOrders[n1]
|
||||
o2 := importPackageGroupOrders[n2]
|
||||
if o1 != 0 && o2 != 0 {
|
||||
return o1 < o2
|
||||
}
|
||||
if o1 == 0 && o2 == 0 {
|
||||
return strings.Compare(n1, n2) < 0
|
||||
}
|
||||
return o1 != 0
|
||||
})
|
||||
|
||||
formattedBlock := bytes.Buffer{}
|
||||
for _, groupName := range groupNames {
|
||||
hasNormalImports := false
|
||||
hasDummyImports := false
|
||||
// non-dummy import comes first
|
||||
for _, importLine := range importGroups[groupName] {
|
||||
if strings.HasPrefix(importLine.content, "_") {
|
||||
hasDummyImports = true
|
||||
} else {
|
||||
formattedBlock.WriteString("\t" + importLine.content + "\n")
|
||||
hasNormalImports = true
|
||||
}
|
||||
}
|
||||
// dummy (_ "pkg") comes later
|
||||
if hasDummyImports {
|
||||
if hasNormalImports {
|
||||
formattedBlock.WriteString("\n")
|
||||
}
|
||||
for _, importLine := range importGroups[groupName] {
|
||||
if strings.HasPrefix(importLine.content, "_") {
|
||||
formattedBlock.WriteString("\t" + importLine.content + "\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
formattedBlock.WriteString("\n")
|
||||
}
|
||||
formattedBlockBytes := bytes.TrimRight(formattedBlock.Bytes(), "\n")
|
||||
|
||||
var formattedBytes []byte
|
||||
formattedBytes = append(formattedBytes, contentBytes[:p1]...)
|
||||
formattedBytes = append(formattedBytes, formattedBlockBytes...)
|
||||
formattedBytes = append(formattedBytes, contentBytes[p2:]...)
|
||||
return formattedBytes, nil
|
||||
}
|
||||
|
||||
// FormatGoImports format the imports by our rules (see unit tests)
|
||||
func FormatGoImports(file string, doChangedFiles, doWriteFile bool) error {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var contentBytes []byte
|
||||
{
|
||||
defer f.Close()
|
||||
contentBytes, err = io.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
formattedBytes, err := formatGoImports(contentBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if formattedBytes == nil {
|
||||
return nil
|
||||
}
|
||||
if bytes.Equal(contentBytes, formattedBytes) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if doChangedFiles {
|
||||
fmt.Println(file)
|
||||
}
|
||||
|
||||
if doWriteFile {
|
||||
f, err = os.OpenFile(file, os.O_TRUNC|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = f.Write(formattedBytes)
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
@@ -1,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,7 @@
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -19,14 +20,14 @@ import (
|
||||
"github.com/shurcooL/vfsgen"
|
||||
)
|
||||
|
||||
func needsUpdate(dir, filename string) (bool, []byte) {
|
||||
func needsUpdate(dir string, filename string) (bool, []byte) {
|
||||
needRegen := false
|
||||
_, err := os.Stat(filename)
|
||||
if err != nil {
|
||||
needRegen = true
|
||||
}
|
||||
|
||||
oldHash, err := os.ReadFile(filename + ".hash")
|
||||
oldHash, err := ioutil.ReadFile(filename + ".hash")
|
||||
if err != nil {
|
||||
oldHash = []byte{}
|
||||
}
|
||||
@@ -49,6 +50,7 @@ func needsUpdate(dir, filename string) (bool, []byte) {
|
||||
newHash := hasher.Sum([]byte{})
|
||||
|
||||
if bytes.Compare(oldHash, newHash) != 0 {
|
||||
|
||||
return true, newHash
|
||||
}
|
||||
|
||||
@@ -56,15 +58,11 @@ func needsUpdate(dir, filename string) (bool, []byte) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 4 {
|
||||
if len(os.Args) != 4 {
|
||||
log.Fatal("Insufficient number of arguments. Need: directory packageName filename")
|
||||
}
|
||||
|
||||
dir, packageName, filename := os.Args[1], os.Args[2], os.Args[3]
|
||||
var useGlobalModTime bool
|
||||
if len(os.Args) == 5 {
|
||||
useGlobalModTime, _ = strconv.ParseBool(os.Args[4])
|
||||
}
|
||||
|
||||
update, newHash := needsUpdate(dir, filename)
|
||||
|
||||
@@ -76,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, 0o666)
|
||||
_ = ioutil.WriteFile(filename+".hash", newHash, 0666)
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
// 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,17 +11,16 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -29,7 +28,9 @@ const (
|
||||
maxUnicodeVersion = 12
|
||||
)
|
||||
|
||||
var flagOut = flag.String("o", "modules/emoji/emoji_data.go", "out")
|
||||
var (
|
||||
flagOut = flag.String("o", "modules/emoji/emoji_data.go", "out")
|
||||
)
|
||||
|
||||
// Gemoji is a set of emoji data.
|
||||
type Gemoji []Emoji
|
||||
@@ -50,6 +51,7 @@ func (e Emoji) MarshalJSON() ([]byte, error) {
|
||||
x.UnicodeVersion = ""
|
||||
x.Description = ""
|
||||
x.SkinTones = false
|
||||
json := jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
return json.Marshal(x)
|
||||
}
|
||||
|
||||
@@ -65,7 +67,7 @@ func main() {
|
||||
}
|
||||
|
||||
// write
|
||||
err = os.WriteFile(*flagOut, buf, 0o644)
|
||||
err = ioutil.WriteFile(*flagOut, buf, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -94,19 +96,20 @@ func generate() ([]byte, error) {
|
||||
defer res.Body.Close()
|
||||
|
||||
// read all
|
||||
body, err := io.ReadAll(res.Body)
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// unmarshal
|
||||
var data Gemoji
|
||||
json := jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
skinTones := make(map[string]string)
|
||||
var skinTones = make(map[string]string)
|
||||
|
||||
skinTones["\U0001f3fb"] = "Light Skin Tone"
|
||||
skinTones["\U0001f3fc"] = "Medium-Light Skin Tone"
|
||||
@@ -116,7 +119,7 @@ func generate() ([]byte, error) {
|
||||
|
||||
var tmp Gemoji
|
||||
|
||||
// filter out emoji that require greater than max unicode version
|
||||
//filter out emoji that require greater than max unicode version
|
||||
for i := range data {
|
||||
val, _ := strconv.ParseFloat(data[i].UnicodeVersion, 64)
|
||||
if int(val) <= maxUnicodeVersion {
|
||||
@@ -155,7 +158,7 @@ func generate() ([]byte, error) {
|
||||
|
||||
// write a JSON file to use with tribute (write before adding skin tones since we can't support them there yet)
|
||||
file, _ := json.Marshal(data)
|
||||
_ = os.WriteFile("assets/emoji.json", file, 0o644)
|
||||
_ = ioutil.WriteFile("assets/emoji.json", file, 0644)
|
||||
|
||||
// Add skin tones to emoji that support it
|
||||
var (
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -32,7 +33,8 @@ func main() {
|
||||
flag.StringVar(&githubApiToken, "token", "", "github api token")
|
||||
flag.Parse()
|
||||
|
||||
file, err := os.CreateTemp(os.TempDir(), prefix)
|
||||
file, err := ioutil.TempFile(os.TempDir(), prefix)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create temp file. %s", err)
|
||||
}
|
||||
@@ -63,6 +65,7 @@ func main() {
|
||||
}
|
||||
|
||||
gz, err := gzip.NewReader(file)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to gunzip the archive. %s", err)
|
||||
}
|
||||
@@ -93,6 +96,7 @@ func main() {
|
||||
}
|
||||
|
||||
out, err := os.Create(path.Join(destination, strings.TrimSuffix(filepath.Base(hdr.Name), ".gitignore")))
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create new file. %s", err)
|
||||
}
|
||||
@@ -109,13 +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, 0o644)
|
||||
err = ioutil.WriteFile(dst, data, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to write new file. %s", err)
|
||||
}
|
||||
|
@@ -1,8 +1,13 @@
|
||||
#!/usr/bin/env node
|
||||
import imageminZopfli from 'imagemin-zopfli';
|
||||
import {optimize} from 'svgo';
|
||||
import {fabric} from 'fabric';
|
||||
import {readFile, writeFile} from 'fs/promises';
|
||||
'use strict';
|
||||
|
||||
const imageminZopfli = require('imagemin-zopfli');
|
||||
const Svgo = require('svgo');
|
||||
const {fabric} = require('fabric');
|
||||
const {readFile, writeFile} = require('fs').promises;
|
||||
const {resolve} = require('path');
|
||||
|
||||
const logoFile = resolve(__dirname, '../assets/logo.svg');
|
||||
|
||||
function exit(err) {
|
||||
if (err) console.error(err);
|
||||
@@ -17,20 +22,16 @@ function loadSvg(svg) {
|
||||
});
|
||||
}
|
||||
|
||||
async function generate(svg, path, {size, bg}) {
|
||||
const outputFile = new URL(path, import.meta.url);
|
||||
|
||||
if (String(outputFile).endsWith('.svg')) {
|
||||
const {data} = optimize(svg, {
|
||||
async function generate(svg, outputFile, {size, bg}) {
|
||||
if (outputFile.endsWith('.svg')) {
|
||||
const svgo = new Svgo({
|
||||
plugins: [
|
||||
'preset-default',
|
||||
'removeDimensions',
|
||||
{
|
||||
name: 'addAttributesToSVGElement',
|
||||
params: {attributes: [{width: size}, {height: size}]}
|
||||
},
|
||||
{removeDimensions: true},
|
||||
{addAttributesToSVGElement: {attributes: [{width: size}, {height: size}]}},
|
||||
],
|
||||
});
|
||||
|
||||
const {data} = await svgo.optimize(svg);
|
||||
await writeFile(outputFile, data);
|
||||
return;
|
||||
}
|
||||
@@ -65,18 +66,17 @@ async function generate(svg, path, {size, bg}) {
|
||||
|
||||
async function main() {
|
||||
const gitea = process.argv.slice(2).includes('gitea');
|
||||
const logoSvg = await readFile(new URL('../assets/logo.svg', import.meta.url), 'utf8');
|
||||
const faviconSvg = await readFile(new URL('../assets/favicon.svg', import.meta.url), 'utf8');
|
||||
const svg = await readFile(logoFile, 'utf8');
|
||||
|
||||
await Promise.all([
|
||||
generate(logoSvg, '../public/img/logo.svg', {size: 32}),
|
||||
generate(logoSvg, '../public/img/logo.png', {size: 512}),
|
||||
generate(faviconSvg, '../public/img/favicon.svg', {size: 32}),
|
||||
generate(faviconSvg, '../public/img/favicon.png', {size: 180}),
|
||||
generate(logoSvg, '../public/img/avatar_default.png', {size: 200}),
|
||||
generate(logoSvg, '../public/img/apple-touch-icon.png', {size: 180, bg: true}),
|
||||
gitea && generate(logoSvg, '../public/img/gitea.svg', {size: 32}),
|
||||
generate(svg, resolve(__dirname, '../public/img/logo.svg'), {size: 32}),
|
||||
generate(svg, resolve(__dirname, '../public/img/logo.png'), {size: 512}),
|
||||
generate(svg, resolve(__dirname, '../public/img/favicon.png'), {size: 180}),
|
||||
generate(svg, resolve(__dirname, '../public/img/avatar_default.png'), {size: 200}),
|
||||
generate(svg, resolve(__dirname, '../public/img/apple-touch-icon.png'), {size: 180, bg: true}),
|
||||
gitea && generate(svg, resolve(__dirname, '../public/img/gitea.svg'), {size: 32}),
|
||||
]);
|
||||
}
|
||||
|
||||
main().then(exit).catch(exit);
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -32,7 +33,8 @@ func main() {
|
||||
flag.StringVar(&githubApiToken, "token", "", "github api token")
|
||||
flag.Parse()
|
||||
|
||||
file, err := os.CreateTemp(os.TempDir(), prefix)
|
||||
file, err := ioutil.TempFile(os.TempDir(), prefix)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create temp file. %s", err)
|
||||
}
|
||||
@@ -64,6 +66,7 @@ func main() {
|
||||
}
|
||||
|
||||
gz, err := gzip.NewReader(file)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to gunzip the archive. %s", err)
|
||||
}
|
||||
@@ -97,6 +100,7 @@ func main() {
|
||||
continue
|
||||
}
|
||||
out, err := os.Create(path.Join(destination, strings.TrimSuffix(filepath.Base(hdr.Name), ".txt")))
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create new file. %s", err)
|
||||
}
|
||||
|
@@ -1,14 +1,13 @@
|
||||
#!/usr/bin/env node
|
||||
import fastGlob from 'fast-glob';
|
||||
import {optimize} from 'svgo';
|
||||
import {parse} from 'path';
|
||||
import {readFile, writeFile, mkdir} from 'fs/promises';
|
||||
import {fileURLToPath} from 'url';
|
||||
'use strict';
|
||||
|
||||
const glob = (pattern) => fastGlob.sync(pattern, {
|
||||
cwd: fileURLToPath(new URL('..', import.meta.url)),
|
||||
absolute: true,
|
||||
});
|
||||
const fastGlob = require('fast-glob');
|
||||
const Svgo = require('svgo');
|
||||
const {resolve, parse} = require('path');
|
||||
const {readFile, writeFile, mkdir} = require('fs').promises;
|
||||
|
||||
const glob = (pattern) => fastGlob.sync(pattern, {cwd: resolve(__dirname), absolute: true});
|
||||
const outputDir = resolve(__dirname, '../public/img/svg');
|
||||
|
||||
function exit(err) {
|
||||
if (err) console.error(err);
|
||||
@@ -17,6 +16,7 @@ function exit(err) {
|
||||
|
||||
async function processFile(file, {prefix, fullName} = {}) {
|
||||
let name;
|
||||
|
||||
if (fullName) {
|
||||
name = fullName;
|
||||
} else {
|
||||
@@ -25,18 +25,32 @@ async function processFile(file, {prefix, fullName} = {}) {
|
||||
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(fileURLToPath(new URL(`../public/img/svg/${name}.svg`, import.meta.url)), data);
|
||||
const {data} = await svgo.optimize(await readFile(file, 'utf8'));
|
||||
await writeFile(resolve(outputDir, `${name}.svg`), data);
|
||||
}
|
||||
|
||||
function processFiles(pattern, opts) {
|
||||
@@ -45,14 +59,15 @@ function processFiles(pattern, opts) {
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
await mkdir(fileURLToPath(new URL('../public/img/svg', import.meta.url)), {recursive: true});
|
||||
await mkdir(outputDir);
|
||||
} catch {}
|
||||
|
||||
await Promise.all([
|
||||
...processFiles('node_modules/@primer/octicons/build/svg/*-16.svg', {prefix: 'octicon'}),
|
||||
...processFiles('web_src/svg/*.svg'),
|
||||
...processFiles('public/img/gitea.svg', {fullName: 'gitea-gitea'}),
|
||||
...processFiles('../node_modules/@primer/octicons/build/svg/*-16.svg', {prefix: 'octicon'}),
|
||||
...processFiles('../web_src/svg/*.svg'),
|
||||
...processFiles('../public/img/gitea.svg', {fullName: 'gitea-gitea'}),
|
||||
]);
|
||||
}
|
||||
|
||||
main().then(exit).catch(exit);
|
||||
|
||||
|
@@ -1,26 +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
|
||||
|
||||
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,7 @@
|
||||
// gocovmerge takes the results from multiple `go test -coverprofile` runs and
|
||||
// merges them into one profile
|
||||
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"golang.org/x/tools/cover"
|
||||
)
|
||||
|
||||
func mergeProfiles(p, merge *cover.Profile) {
|
||||
func mergeProfiles(p *cover.Profile, merge *cover.Profile) {
|
||||
if p.Mode != merge.Mode {
|
||||
log.Fatalf("cannot merge profiles with different modes")
|
||||
}
|
||||
@@ -108,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 .
|
469
cmd/admin.go
469
cmd/admin.go
@@ -14,24 +14,13 @@ import (
|
||||
"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"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
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"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
auth_service "code.gitea.io/gitea/services/auth"
|
||||
"code.gitea.io/gitea/services/auth/source/oauth2"
|
||||
"code.gitea.io/gitea/services/auth/source/smtp"
|
||||
repo_service "code.gitea.io/gitea/services/repository"
|
||||
user_service "code.gitea.io/gitea/services/user"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
@@ -58,7 +47,6 @@ var (
|
||||
microcmdUserList,
|
||||
microcmdUserChangePassword,
|
||||
microcmdUserDelete,
|
||||
microcmdUserGenerateAccessToken,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -116,10 +104,6 @@ var (
|
||||
Name: "access-token",
|
||||
Usage: "Generate access token for the user",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "restricted",
|
||||
Usage: "Make a restricted user account",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -161,27 +145,6 @@ var (
|
||||
Action: runDeleteUser,
|
||||
}
|
||||
|
||||
microcmdUserGenerateAccessToken = cli.Command{
|
||||
Name: "generate-access-token",
|
||||
Usage: "Generate a access token for a specific user",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "username,u",
|
||||
Usage: "Username",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "token-name,t",
|
||||
Usage: "Token name",
|
||||
Value: "gitea-admin",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "raw",
|
||||
Usage: "Display only the token value",
|
||||
},
|
||||
},
|
||||
Action: runGenerateAccessToken,
|
||||
}
|
||||
|
||||
subcmdRepoSyncReleases = cli.Command{
|
||||
Name: "repo-sync-releases",
|
||||
Usage: "Synchronize repository releases with tags",
|
||||
@@ -219,8 +182,6 @@ var (
|
||||
cmdAuthUpdateLdapBindDn,
|
||||
cmdAuthAddLdapSimpleAuth,
|
||||
cmdAuthUpdateLdapSimpleAuth,
|
||||
microcmdAuthAddSMTP,
|
||||
microcmdAuthUpdateSMTP,
|
||||
microcmdAuthList,
|
||||
microcmdAuthDelete,
|
||||
},
|
||||
@@ -326,40 +287,6 @@ var (
|
||||
Value: "",
|
||||
Usage: "Custom icon URL for OAuth2 login source",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "skip-local-2fa",
|
||||
Usage: "Set to true to skip local 2fa for users authenticated by this source",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "scopes",
|
||||
Value: nil,
|
||||
Usage: "Scopes to request when to authenticate against this OAuth2 source",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "required-claim-name",
|
||||
Value: "",
|
||||
Usage: "Claim name that has to be set to allow users to login with this source",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "required-claim-value",
|
||||
Value: "",
|
||||
Usage: "Claim value that has to be set to allow users to login with this source",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "group-claim-name",
|
||||
Value: "",
|
||||
Usage: "Claim name providing group names for this source",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "admin-group",
|
||||
Value: "",
|
||||
Usage: "Group Claim value for administrator users",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "restricted-group",
|
||||
Value: "",
|
||||
Usage: "Group Claim value for restricted users",
|
||||
},
|
||||
}
|
||||
|
||||
microcmdAuthUpdateOauth = cli.Command{
|
||||
@@ -397,72 +324,6 @@ var (
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
smtpCLIFlags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "name",
|
||||
Value: "",
|
||||
Usage: "Application Name",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "auth-type",
|
||||
Value: "PLAIN",
|
||||
Usage: "SMTP Authentication Type (PLAIN/LOGIN/CRAM-MD5) default PLAIN",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "host",
|
||||
Value: "",
|
||||
Usage: "SMTP Host",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "port",
|
||||
Usage: "SMTP Port",
|
||||
},
|
||||
cli.BoolTFlag{
|
||||
Name: "force-smtps",
|
||||
Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.",
|
||||
},
|
||||
cli.BoolTFlag{
|
||||
Name: "skip-verify",
|
||||
Usage: "Skip TLS verify.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "helo-hostname",
|
||||
Value: "",
|
||||
Usage: "Hostname sent with HELO. Leave blank to send current hostname",
|
||||
},
|
||||
cli.BoolTFlag{
|
||||
Name: "disable-helo",
|
||||
Usage: "Disable SMTP helo.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "allowed-domains",
|
||||
Value: "",
|
||||
Usage: "Leave empty to allow all domains. Separate multiple domains with a comma (',')",
|
||||
},
|
||||
cli.BoolTFlag{
|
||||
Name: "skip-local-2fa",
|
||||
Usage: "Skip 2FA to log on.",
|
||||
},
|
||||
cli.BoolTFlag{
|
||||
Name: "active",
|
||||
Usage: "This Authentication Source is Activated.",
|
||||
},
|
||||
}
|
||||
|
||||
microcmdAuthAddSMTP = cli.Command{
|
||||
Name: "add-smtp",
|
||||
Usage: "Add new SMTP authentication source",
|
||||
Action: runAddSMTP,
|
||||
Flags: smtpCLIFlags,
|
||||
}
|
||||
|
||||
microcmdAuthUpdateSMTP = cli.Command{
|
||||
Name: "update-smtp",
|
||||
Usage: "Update existing SMTP authentication source",
|
||||
Action: runUpdateSMTP,
|
||||
Flags: append(smtpCLIFlags[:1], append([]cli.Flag{idFlag}, smtpCLIFlags[1:]...)...),
|
||||
}
|
||||
)
|
||||
|
||||
func runChangePassword(c *cli.Context) error {
|
||||
@@ -470,16 +331,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")
|
||||
}
|
||||
@@ -491,7 +345,7 @@ func runChangePassword(c *cli.Context) error {
|
||||
return errors.New("The password you chose is on a list of stolen passwords previously exposed in public data breaches. Please try again with a different password.\nFor more details, see https://haveibeenpwned.com/Passwords")
|
||||
}
|
||||
uname := c.String("username")
|
||||
user, err := user_model.GetUserByName(ctx, uname)
|
||||
user, err := models.GetUserByName(uname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -499,7 +353,7 @@ func runChangePassword(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = user_model.UpdateUserCols(ctx, user, "passwd", "passwd_hash_algo", "salt"); err != nil {
|
||||
if err = models.UpdateUserCols(user, "passwd", "passwd_hash_algo", "salt"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -531,10 +385,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
|
||||
}
|
||||
|
||||
@@ -553,11 +404,11 @@ func runCreateUser(c *cli.Context) error {
|
||||
}
|
||||
|
||||
// always default to true
|
||||
changePassword := true
|
||||
var changePassword = true
|
||||
|
||||
// If this is the first user being created.
|
||||
// Take it as the admin and don't force a password update.
|
||||
if n := user_model.CountUsers(nil); n == 0 {
|
||||
if n := models.CountUsers(); n == 0 {
|
||||
changePassword = false
|
||||
}
|
||||
|
||||
@@ -565,26 +416,17 @@ func runCreateUser(c *cli.Context) error {
|
||||
changePassword = c.Bool("must-change-password")
|
||||
}
|
||||
|
||||
restricted := util.OptionalBoolNone
|
||||
|
||||
if c.IsSet("restricted") {
|
||||
restricted = util.OptionalBoolOf(c.Bool("restricted"))
|
||||
}
|
||||
|
||||
u := &user_model.User{
|
||||
u := &models.User{
|
||||
Name: username,
|
||||
Email: c.String("email"),
|
||||
Passwd: password,
|
||||
IsActive: true,
|
||||
IsAdmin: c.Bool("admin"),
|
||||
MustChangePassword: changePassword,
|
||||
Theme: setting.UI.DefaultTheme,
|
||||
}
|
||||
|
||||
overwriteDefault := &user_model.CreateUserOverwriteOptions{
|
||||
IsActive: util.OptionalBoolTrue,
|
||||
IsRestricted: restricted,
|
||||
}
|
||||
|
||||
if err := user_model.CreateUser(u, overwriteDefault); err != nil {
|
||||
if err := models.CreateUser(u); err != nil {
|
||||
return fmt.Errorf("CreateUser: %v", err)
|
||||
}
|
||||
|
||||
@@ -606,14 +448,12 @@ func runCreateUser(c *cli.Context) error {
|
||||
}
|
||||
|
||||
func runListUsers(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
users, err := user_model.GetAllUsers()
|
||||
users, err := models.GetAllUsers()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -637,6 +477,7 @@ func runListUsers(c *cli.Context) error {
|
||||
|
||||
w.Flush()
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func runDeleteUser(c *cli.Context) error {
|
||||
@@ -644,25 +485,18 @@ func runDeleteUser(c *cli.Context) error {
|
||||
return fmt.Errorf("You must provide the id, username or email of a user to delete")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := storage.Init(); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var err error
|
||||
var user *user_model.User
|
||||
var user *models.User
|
||||
if c.IsSet("email") {
|
||||
user, err = user_model.GetUserByEmail(c.String("email"))
|
||||
user, err = models.GetUserByEmail(c.String("email"))
|
||||
} else if c.IsSet("username") {
|
||||
user, err = user_model.GetUserByName(ctx, c.String("username"))
|
||||
user, err = models.GetUserByName(c.String("username"))
|
||||
} else {
|
||||
user, err = user_model.GetUserByID(c.Int64("id"))
|
||||
user, err = models.GetUserByID(c.Int64("id"))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -675,57 +509,19 @@ func runDeleteUser(c *cli.Context) error {
|
||||
return fmt.Errorf("The user %s does not match the provided id %d", user.Name, c.Int64("id"))
|
||||
}
|
||||
|
||||
return user_service.DeleteUser(user)
|
||||
return models.DeleteUser(user)
|
||||
}
|
||||
|
||||
func runGenerateAccessToken(c *cli.Context) error {
|
||||
if !c.IsSet("username") {
|
||||
return fmt.Errorf("You must provide the username to generate a token for them")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user, err := user_model.GetUserByName(ctx, c.String("username"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t := &models.AccessToken{
|
||||
Name: c.String("token-name"),
|
||||
UID: user.ID,
|
||||
}
|
||||
|
||||
if err := models.NewAccessToken(t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Bool("raw") {
|
||||
fmt.Printf("%s\n", t.Token)
|
||||
} else {
|
||||
fmt.Printf("Access token was successfully created: %s\n", t.Token)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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 := repo_model.SearchRepositoryByName(&repo_model.SearchRepoOptions{
|
||||
ListOptions: db.ListOptions{
|
||||
PageSize: repo_model.RepositoryListDefaultPageSize,
|
||||
repos, count, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
|
||||
ListOptions: models.ListOptions{
|
||||
PageSize: models.RepositoryListDefaultPageSize,
|
||||
Page: page,
|
||||
},
|
||||
Private: true,
|
||||
@@ -739,7 +535,7 @@ func runRepoSyncReleases(_ *cli.Context) error {
|
||||
log.Trace("Processing next %d repos of %d", len(repos), count)
|
||||
for _, repo := range repos {
|
||||
log.Trace("Synchronizing repo %s with path %s", repo.FullName(), repo.RepoPath())
|
||||
gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())
|
||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||
if err != nil {
|
||||
log.Warn("OpenRepository: %v", err)
|
||||
continue
|
||||
@@ -782,27 +578,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{
|
||||
@@ -814,36 +604,26 @@ func parseOAuth2Config(c *cli.Context) *oauth2.Source {
|
||||
} else {
|
||||
customURLMapping = nil
|
||||
}
|
||||
return &oauth2.Source{
|
||||
return &models.OAuth2Config{
|
||||
Provider: c.String("provider"),
|
||||
ClientID: c.String("key"),
|
||||
ClientSecret: c.String("secret"),
|
||||
OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
|
||||
CustomURLMapping: customURLMapping,
|
||||
IconURL: c.String("icon-url"),
|
||||
SkipLocalTwoFA: c.Bool("skip-local-2fa"),
|
||||
Scopes: c.StringSlice("scopes"),
|
||||
RequiredClaimName: c.String("required-claim-name"),
|
||||
RequiredClaimValue: c.String("required-claim-value"),
|
||||
GroupClaimName: c.String("group-claim-name"),
|
||||
AdminGroup: c.String("admin-group"),
|
||||
RestrictedGroup: c.String("restricted-group"),
|
||||
}
|
||||
}
|
||||
|
||||
func runAddOauth(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return auth.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),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -852,19 +632,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")
|
||||
@@ -890,29 +667,8 @@ func runUpdateOauth(c *cli.Context) error {
|
||||
oAuth2Config.IconURL = c.String("icon-url")
|
||||
}
|
||||
|
||||
if c.IsSet("scopes") {
|
||||
oAuth2Config.Scopes = c.StringSlice("scopes")
|
||||
}
|
||||
|
||||
if c.IsSet("required-claim-name") {
|
||||
oAuth2Config.RequiredClaimName = c.String("required-claim-name")
|
||||
}
|
||||
if c.IsSet("required-claim-value") {
|
||||
oAuth2Config.RequiredClaimValue = c.String("required-claim-value")
|
||||
}
|
||||
|
||||
if c.IsSet("group-claim-name") {
|
||||
oAuth2Config.GroupClaimName = c.String("group-claim-name")
|
||||
}
|
||||
if c.IsSet("admin-group") {
|
||||
oAuth2Config.AdminGroup = c.String("admin-group")
|
||||
}
|
||||
if c.IsSet("restricted-group") {
|
||||
oAuth2Config.RestrictedGroup = c.String("restricted-group")
|
||||
}
|
||||
|
||||
// update custom URL mapping
|
||||
customURLMapping := &oauth2.CustomURLMapping{}
|
||||
var customURLMapping = &oauth2.CustomURLMapping{}
|
||||
|
||||
if oAuth2Config.CustomURLMapping != nil {
|
||||
customURLMapping.TokenURL = oAuth2Config.CustomURLMapping.TokenURL
|
||||
@@ -939,130 +695,16 @@ 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")
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -1080,8 +722,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()
|
||||
|
||||
@@ -1093,17 +735,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,97 +159,91 @@ var (
|
||||
// newAuthService creates a service with default functions.
|
||||
func newAuthService() *authService {
|
||||
return &authService{
|
||||
initDB: initDB,
|
||||
createAuthSource: auth.CreateSource,
|
||||
updateAuthSource: auth.UpdateSource,
|
||||
getAuthSourceByID: auth.GetSourceByID,
|
||||
initDB: initDB,
|
||||
createLoginSource: models.CreateLoginSource,
|
||||
updateLoginSource: models.UpdateSource,
|
||||
getLoginSourceByID: models.GetLoginSourceByID,
|
||||
}
|
||||
}
|
||||
|
||||
// parseAuthSource assigns values on authSource according to command line flags.
|
||||
func parseAuthSource(c *cli.Context, authSource *auth.Source) {
|
||||
// parseLoginSource assigns values on loginSource according to command line flags.
|
||||
func parseLoginSource(c *cli.Context, loginSource *models.LoginSource) {
|
||||
if c.IsSet("name") {
|
||||
authSource.Name = c.String("name")
|
||||
loginSource.Name = c.String("name")
|
||||
}
|
||||
if c.IsSet("not-active") {
|
||||
authSource.IsActive = !c.Bool("not-active")
|
||||
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")
|
||||
}
|
||||
if c.IsSet("skip-local-2fa") {
|
||||
config.SkipLocalTwoFA = c.Bool("skip-local-2fa")
|
||||
config.Source.AllowDeactivateAll = c.Bool("allow-deactivate-all")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -266,7 +251,7 @@ func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
|
||||
// findLdapSecurityProtocolByName finds security protocol by its name ignoring case.
|
||||
// It returns the value of the security protocol and if it was found.
|
||||
func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
|
||||
for i, n := range ldap.SecurityProtocolNames {
|
||||
for i, n := range models.SecurityProtocolNames {
|
||||
if strings.EqualFold(name, n) {
|
||||
return i, true
|
||||
}
|
||||
@@ -274,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.
|
||||
@@ -299,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.
|
||||
@@ -350,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
@@ -180,7 +180,7 @@ func runCert(c *cli.Context) error {
|
||||
}
|
||||
log.Println("Written cert.pem")
|
||||
|
||||
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
|
||||
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open key.pem for writing: %v", err)
|
||||
}
|
||||
|
52
cmd/cmd.go
52
cmd/cmd.go
@@ -7,16 +7,11 @@
|
||||
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"
|
||||
|
||||
@@ -31,7 +26,7 @@ func argsSet(c *cli.Context, args ...string) error {
|
||||
return errors.New(a + " is not set")
|
||||
}
|
||||
|
||||
if util.IsEmptyString(c.String(a)) {
|
||||
if util.IsEmptyString(a) {
|
||||
return errors.New(a + " is required")
|
||||
}
|
||||
}
|
||||
@@ -57,40 +52,17 @@ func confirm() (bool, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func initDB(ctx context.Context) error {
|
||||
setting.LoadFromExisting()
|
||||
setting.InitDBConfig()
|
||||
setting.NewXORMLogService(false)
|
||||
func initDB() error {
|
||||
return initDBDisableConsole(false)
|
||||
}
|
||||
|
||||
if setting.Database.Type == "" {
|
||||
log.Fatal(`Database settings are missing from the configuration file: %q.
|
||||
Ensure you are running in the correct environment or set the correct configuration file with -c.
|
||||
If this is the intended configuration file complete the [database] section.`, setting.CustomConf)
|
||||
}
|
||||
if err := db.InitEngine(ctx); err != nil {
|
||||
return fmt.Errorf("unable to initialize the database using the configuration in %q. Error: %v", setting.CustomConf, err)
|
||||
func initDBDisableConsole(disableConsole bool) error {
|
||||
setting.NewContext()
|
||||
setting.InitDBConfig()
|
||||
|
||||
setting.NewXORMLogService(disableConsole)
|
||||
if err := models.SetEngine(); err != nil {
|
||||
return fmt.Errorf("models.SetEngine: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@@ -43,11 +43,7 @@ func runDocs(ctx *cli.Context) error {
|
||||
// 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:]
|
||||
}
|
||||
docs = docs[strings.Index(docs, "#"):]
|
||||
}
|
||||
|
||||
out := os.Stdout
|
||||
|
@@ -5,28 +5,29 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"context"
|
||||
"fmt"
|
||||
golog "log"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/migrations"
|
||||
"code.gitea.io/gitea/modules/doctor"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"xorm.io/xorm"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// CmdDoctor represents the available doctor sub-command.
|
||||
var CmdDoctor = cli.Command{
|
||||
Name: "doctor",
|
||||
Usage: "Diagnose and optionally fix problems",
|
||||
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
|
||||
Usage: "Diagnose problems",
|
||||
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration.",
|
||||
Action: runDoctor,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
@@ -87,7 +88,7 @@ func runRecreateTable(ctx *cli.Context) error {
|
||||
golog.SetPrefix("")
|
||||
golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT)))
|
||||
|
||||
setting.LoadFromExisting()
|
||||
setting.NewContext()
|
||||
setting.InitDBConfig()
|
||||
|
||||
setting.EnableXORMLog = ctx.Bool("debug")
|
||||
@@ -95,10 +96,7 @@ func runRecreateTable(ctx *cli.Context) error {
|
||||
setting.Cfg.Section("log").Key("XORM").SetValue(",")
|
||||
|
||||
setting.NewXORMLogService(!ctx.Bool("debug"))
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := db.InitEngine(stdCtx); err != nil {
|
||||
if err := models.SetEngine(); err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.")
|
||||
return nil
|
||||
@@ -110,25 +108,33 @@ 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(stdCtx, func(x *xorm.Engine) error {
|
||||
return models.NewEngine(context.Background(), func(x *xorm.Engine) error {
|
||||
if err := migrations.EnsureUpToDate(x); err != nil {
|
||||
return err
|
||||
}
|
||||
return recreateTables(x)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func setDoctorLogger(ctx *cli.Context) {
|
||||
func runDoctor(ctx *cli.Context) error {
|
||||
|
||||
// Silence the default loggers
|
||||
log.DelNamedLogger("console")
|
||||
log.DelNamedLogger(log.DEFAULT)
|
||||
|
||||
// Now setup our own
|
||||
logFile := ctx.String("log-file")
|
||||
if !ctx.IsSet("log-file") {
|
||||
logFile = "doctor.log"
|
||||
}
|
||||
|
||||
colorize := log.CanColorStdout
|
||||
if ctx.IsSet("color") {
|
||||
colorize = ctx.Bool("color")
|
||||
@@ -136,50 +142,11 @@ func setDoctorLogger(ctx *cli.Context) {
|
||||
|
||||
if len(logFile) == 0 {
|
||||
log.NewLogger(1000, "doctor", "console", fmt.Sprintf(`{"level":"NONE","stacktracelevel":"NONE","colorize":%t}`, colorize))
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
recovered := recover()
|
||||
if recovered == nil {
|
||||
return
|
||||
}
|
||||
|
||||
err, ok := recovered.(error)
|
||||
if !ok {
|
||||
panic(recovered)
|
||||
}
|
||||
if errors.Is(err, os.ErrPermission) {
|
||||
fmt.Fprintf(os.Stderr, "ERROR: Unable to write logs to provided file due to permissions error: %s\n %v\n", logFile, err)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "ERROR: Unable to write logs to provided file: %s\n %v\n", logFile, err)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "WARN: Logging will be disabled\n Use `--log-file` to configure log file location\n")
|
||||
log.NewLogger(1000, "doctor", "console", fmt.Sprintf(`{"level":"NONE","stacktracelevel":"NONE","colorize":%t}`, colorize))
|
||||
}()
|
||||
|
||||
if logFile == "-" {
|
||||
} else if logFile == "-" {
|
||||
log.NewLogger(1000, "doctor", "console", fmt.Sprintf(`{"level":"trace","stacktracelevel":"NONE","colorize":%t}`, colorize))
|
||||
} else {
|
||||
log.NewLogger(1000, "doctor", "file", fmt.Sprintf(`{"filename":%q,"level":"trace","stacktracelevel":"NONE"}`, logFile))
|
||||
}
|
||||
}
|
||||
|
||||
func runDoctor(ctx *cli.Context) error {
|
||||
// Silence the default loggers
|
||||
log.DelNamedLogger("console")
|
||||
log.DelNamedLogger(log.DEFAULT)
|
||||
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
// Now setup our own
|
||||
setDoctorLogger(ctx)
|
||||
|
||||
colorize := log.CanColorStdout
|
||||
if ctx.IsSet("color") {
|
||||
colorize = ctx.Bool("color")
|
||||
}
|
||||
|
||||
// Finally redirect the default golog to here
|
||||
golog.SetFlags(0)
|
||||
@@ -234,7 +201,7 @@ func runDoctor(ctx *cli.Context) error {
|
||||
|
||||
// Now we can set up our own logger to return information about what the doctor is doing
|
||||
if err := log.NewNamedLogger("doctorouter",
|
||||
0,
|
||||
1000,
|
||||
"console",
|
||||
"console",
|
||||
fmt.Sprintf(`{"level":"INFO","stacktracelevel":"NONE","colorize":%t,"flags":-1}`, colorize)); err != nil {
|
||||
@@ -244,5 +211,5 @@ func runDoctor(ctx *cli.Context) error {
|
||||
|
||||
logger := log.GetLogger("doctorouter")
|
||||
defer logger.Close()
|
||||
return doctor.RunChecks(stdCtx, logger, ctx.Bool("fix"), checks)
|
||||
return doctor.RunChecks(logger, ctx.Bool("fix"), checks)
|
||||
}
|
||||
|
131
cmd/dump.go
131
cmd/dump.go
@@ -7,40 +7,29 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"gitea.com/go-chi/session"
|
||||
"github.com/mholt/archiver/v3"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
archiver "github.com/mholt/archiver/v3"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func addReader(w archiver.Writer, r io.ReadCloser, info os.FileInfo, customName string, verbose bool) error {
|
||||
func addFile(w archiver.Writer, filePath string, absPath string, verbose bool) error {
|
||||
if verbose {
|
||||
log.Info("Adding file %s", customName)
|
||||
log.Info("Adding file %s\n", filePath)
|
||||
}
|
||||
|
||||
return w.Write(archiver.File{
|
||||
FileInfo: archiver.FileInfo{
|
||||
FileInfo: info,
|
||||
CustomName: customName,
|
||||
},
|
||||
ReadCloser: r,
|
||||
})
|
||||
}
|
||||
|
||||
func addFile(w archiver.Writer, filePath, absPath string, verbose bool) error {
|
||||
file, err := os.Open(absPath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -51,10 +40,16 @@ func addFile(w archiver.Writer, filePath, absPath string, verbose bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return addReader(w, file, fileInfo, filePath, verbose)
|
||||
return w.Write(archiver.File{
|
||||
FileInfo: archiver.FileInfo{
|
||||
FileInfo: fileInfo,
|
||||
CustomName: filePath,
|
||||
},
|
||||
ReadCloser: file,
|
||||
})
|
||||
}
|
||||
|
||||
func isSubdir(upper, lower string) (bool, error) {
|
||||
func isSubdir(upper string, lower string) (bool, error) {
|
||||
if relPath, err := filepath.Rel(upper, lower); err != nil {
|
||||
return false, err
|
||||
} else if relPath == "." || !strings.HasPrefix(relPath, ".") {
|
||||
@@ -92,7 +87,7 @@ func (o outputType) String() string {
|
||||
}
|
||||
|
||||
var outputTypeEnum = &outputType{
|
||||
Enum: []string{"zip", "tar", "tar.sz", "tar.gz", "tar.xz", "tar.bz2", "tar.br", "tar.lz4"},
|
||||
Enum: []string{"zip", "tar", "tar.gz", "tar.xz", "tar.bz2"},
|
||||
Default: "zip",
|
||||
}
|
||||
|
||||
@@ -134,18 +129,6 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
|
||||
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.BoolFlag{
|
||||
Name: "skip-package-data",
|
||||
Usage: "Skip package data",
|
||||
},
|
||||
cli.GenericFlag{
|
||||
Name: "type",
|
||||
Value: outputTypeEnum,
|
||||
@@ -162,24 +145,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 {
|
||||
for _, suffix := range outputTypeEnum.Enum {
|
||||
if strings.HasSuffix(fileName, "."+suffix) {
|
||||
fileName = strings.TrimSuffix(fileName, "."+suffix)
|
||||
break
|
||||
}
|
||||
}
|
||||
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)
|
||||
@@ -193,10 +166,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
|
||||
}
|
||||
@@ -219,6 +189,7 @@ func runDump(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
verbose := ctx.Bool("verbose")
|
||||
outType := ctx.String("type")
|
||||
var iface interface{}
|
||||
if fileName == "-" {
|
||||
iface, err = archiver.ByExtension(fmt.Sprintf(".%s", outType))
|
||||
@@ -243,15 +214,19 @@ func runDump(ctx *cli.Context) error {
|
||||
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
|
||||
}
|
||||
|
||||
return addReader(w, object, info, path.Join("data", "lfs", objPath), verbose)
|
||||
return w.Write(archiver.File{
|
||||
FileInfo: archiver.FileInfo{
|
||||
FileInfo: info,
|
||||
CustomName: path.Join("data", "lfs", objPath),
|
||||
},
|
||||
ReadCloser: object,
|
||||
})
|
||||
}); err != nil {
|
||||
fatal("Failed to dump LFS objects: %v", err)
|
||||
}
|
||||
@@ -262,7 +237,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)
|
||||
}
|
||||
@@ -279,7 +254,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)
|
||||
}
|
||||
|
||||
@@ -295,7 +270,7 @@ func runDump(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
if ctx.IsSet("skip-custom-dir") && ctx.Bool("skip-custom-dir") {
|
||||
log.Info("Skipping custom directory")
|
||||
log.Info("Skiping custom directory")
|
||||
} else {
|
||||
customDir, err := os.Stat(setting.CustomPath)
|
||||
if err == nil && customDir.IsDir() {
|
||||
@@ -321,6 +296,7 @@ func runDump(ctx *cli.Context) error {
|
||||
var excludes []string
|
||||
if setting.Cfg.Section("session").Key("PROVIDER").Value() == "file" {
|
||||
var opts session.Options
|
||||
json := jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
if err = json.Unmarshal([]byte(setting.SessionConfig.ProviderConfig), &opts); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -330,7 +306,6 @@ func runDump(ctx *cli.Context) error {
|
||||
excludes = append(excludes, setting.RepoRootPath)
|
||||
excludes = append(excludes, setting.LFS.Path)
|
||||
excludes = append(excludes, setting.Attachment.Path)
|
||||
excludes = append(excludes, setting.Packages.Path)
|
||||
excludes = append(excludes, setting.LogRootPath)
|
||||
excludes = append(excludes, absFileName)
|
||||
if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil {
|
||||
@@ -338,32 +313,23 @@ func runDump(ctx *cli.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return addReader(w, object, info, path.Join("data", "attachments", objPath), verbose)
|
||||
return w.Write(archiver.File{
|
||||
FileInfo: archiver.FileInfo{
|
||||
FileInfo: info,
|
||||
CustomName: path.Join("data", "attachments", objPath),
|
||||
},
|
||||
ReadCloser: object,
|
||||
})
|
||||
}); err != nil {
|
||||
fatal("Failed to dump attachments: %v", err)
|
||||
}
|
||||
|
||||
if ctx.IsSet("skip-package-data") && ctx.Bool("skip-package-data") {
|
||||
log.Info("Skip dumping package data")
|
||||
} else if err := storage.Packages.IterateObjects(func(objPath string, object storage.Object) error {
|
||||
info, err := object.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return addReader(w, object, info, path.Join("data", "packages", objPath), verbose)
|
||||
}); err != nil {
|
||||
fatal("Failed to dump packages: %v", err)
|
||||
}
|
||||
|
||||
// Doesn't check if LogRootPath exists before processing --skip-log intentionally,
|
||||
// ensuring that it's clear the dump is skipped whether the directory's initialized
|
||||
// yet or not.
|
||||
@@ -387,7 +353,7 @@ func runDump(ctx *cli.Context) error {
|
||||
fatal("Failed to save %s: %v", fileName, err)
|
||||
}
|
||||
|
||||
if err := os.Chmod(fileName, 0o600); err != nil {
|
||||
if err := os.Chmod(fileName, 0600); err != nil {
|
||||
log.Info("Can't change file access permissions mask to 0600: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -439,23 +405,8 @@ func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeA
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// only copy regular files and symlink regular files, skip non-regular files like socket/pipe/...
|
||||
shouldAdd := file.Mode().IsRegular()
|
||||
if !shouldAdd && file.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
target, err := filepath.EvalSymlinks(currentAbsPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
targetStat, err := os.Stat(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
shouldAdd = targetStat.Mode().IsRegular()
|
||||
}
|
||||
if shouldAdd {
|
||||
if err = addFile(w, currentInsidePath, currentAbsPath, verbose); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = addFile(w, currentInsidePath, currentAbsPath, verbose); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,18 +7,14 @@ package cmd
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/convert"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
base "code.gitea.io/gitea/modules/migration"
|
||||
"code.gitea.io/gitea/modules/migrations"
|
||||
"code.gitea.io/gitea/modules/migrations/base"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/migrations"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
@@ -73,30 +69,22 @@ var CmdDumpRepository = cli.Command{
|
||||
cli.StringFlag{
|
||||
Name: "units",
|
||||
Value: "",
|
||||
Usage: `Which items will be migrated, one or more units should be separated as comma.
|
||||
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 {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// migrations.GiteaLocalUploader depends on git module
|
||||
if err := git.InitSimple(context.Background()); 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()
|
||||
|
||||
var (
|
||||
serviceType structs.GitServiceType
|
||||
@@ -116,7 +104,7 @@ func runDumpRepository(ctx *cli.Context) error {
|
||||
}
|
||||
serviceType = convert.ToGitServiceType(serviceStr)
|
||||
|
||||
opts := base.MigrateOptions{
|
||||
var opts = base.MigrateOptions{
|
||||
GitServiceType: serviceType,
|
||||
CloneAddr: cloneAddr,
|
||||
AuthUsername: ctx.String("auth_username"),
|
||||
@@ -137,9 +125,7 @@ func runDumpRepository(ctx *cli.Context) error {
|
||||
} else {
|
||||
units := strings.Split(ctx.String("units"), ",")
|
||||
for _, unit := range units {
|
||||
switch strings.ToLower(strings.TrimSpace(unit)) {
|
||||
case "":
|
||||
continue
|
||||
switch strings.ToLower(unit) {
|
||||
case "wiki":
|
||||
opts.Wiki = true
|
||||
case "issues":
|
||||
@@ -156,29 +142,13 @@ func runDumpRepository(ctx *cli.Context) error {
|
||||
opts.Comments = true
|
||||
case "pull_requests":
|
||||
opts.PullRequests = true
|
||||
default:
|
||||
return errors.New("invalid unit: " + unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the repo_dir will be removed if error occurs in DumpRepository
|
||||
// make sure the directory doesn't exist or is empty, prevent from deleting user files
|
||||
repoDir := ctx.String("repo_dir")
|
||||
if exists, err := util.IsExist(repoDir); err != nil {
|
||||
return fmt.Errorf("unable to stat repo_dir %q: %v", repoDir, err)
|
||||
} else if exists {
|
||||
if isDir, _ := util.IsDir(repoDir); !isDir {
|
||||
return fmt.Errorf("repo_dir %q already exists but it's not a directory", repoDir)
|
||||
}
|
||||
if dir, _ := os.ReadDir(repoDir); len(dir) > 0 {
|
||||
return fmt.Errorf("repo_dir %q is not empty", repoDir)
|
||||
}
|
||||
}
|
||||
|
||||
if err := migrations.DumpRepository(
|
||||
context.Background(),
|
||||
repoDir,
|
||||
ctx.String("repo_dir"),
|
||||
ctx.String("owner_name"),
|
||||
opts,
|
||||
); err != nil {
|
||||
|
@@ -2,7 +2,7 @@
|
||||
// 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
|
||||
|
||||
@@ -19,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"
|
||||
@@ -108,12 +107,13 @@ type asset struct {
|
||||
}
|
||||
|
||||
func initEmbeddedExtractor(c *cli.Context) error {
|
||||
|
||||
// Silence the console logger
|
||||
log.DelNamedLogger("console")
|
||||
log.DelNamedLogger(log.DEFAULT)
|
||||
|
||||
// Read configuration file
|
||||
setting.LoadAllowEmpty()
|
||||
setting.NewContext()
|
||||
|
||||
pats, err := getPatterns(c.Args())
|
||||
if err != nil {
|
||||
@@ -258,7 +258,7 @@ func extractAsset(d string, a asset, overwrite, rename bool) error {
|
||||
return fmt.Errorf("%s: %v", dir, err)
|
||||
}
|
||||
|
||||
perms := os.ModePerm & 0o666
|
||||
perms := os.ModePerm & 0666
|
||||
|
||||
fi, err := os.Lstat(dest)
|
||||
if err != nil {
|
||||
@@ -271,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)
|
||||
@@ -294,7 +294,7 @@ func extractAsset(d string, a asset, overwrite, rename bool) error {
|
||||
}
|
||||
|
||||
func buildAssetList(sec *section, globs []glob.Glob, c *cli.Context) []asset {
|
||||
results := make([]asset, 0, 64)
|
||||
var results = make([]asset, 0, 64)
|
||||
for _, name := range sec.Names() {
|
||||
if isdir, err := sec.IsDir(name); !isdir && err == nil {
|
||||
if sec.Path == "public" &&
|
||||
@@ -305,11 +305,9 @@ func buildAssetList(sec *section, globs []glob.Glob, c *cli.Context) []asset {
|
||||
matchName := sec.Path + "/" + name
|
||||
for _, g := range globs {
|
||||
if g.Match(matchName) {
|
||||
results = append(results, asset{
|
||||
Section: sec,
|
||||
Name: name,
|
||||
Path: sec.Path + "/" + name,
|
||||
})
|
||||
results = append(results, asset{Section: sec,
|
||||
Name: name,
|
||||
Path: sec.Path + "/" + name})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
// 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
|
||||
}
|
||||
|
438
cmd/hook.go
438
cmd/hook.go
@@ -15,9 +15,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
@@ -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 {
|
||||
@@ -162,30 +149,29 @@ func (n *nilWriter) WriteString(s string) (int, error) {
|
||||
}
|
||||
|
||||
func runHookPreReceive(c *cli.Context) error {
|
||||
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
|
||||
if os.Getenv(models.EnvIsInternal) == "true" {
|
||||
return nil
|
||||
}
|
||||
ctx, cancel := installSignals()
|
||||
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, _ := strconv.ParseBool(os.Getenv(repo_module.EnvRepoIsWiki))
|
||||
username := os.Getenv(repo_module.EnvRepoUsername)
|
||||
reponame := os.Getenv(repo_module.EnvRepoName)
|
||||
userID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
|
||||
prID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPRID), 10, 64)
|
||||
deployKeyID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvDeployKeyID), 10, 64)
|
||||
// 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)
|
||||
prID, _ := strconv.ParseInt(os.Getenv(models.EnvPRID), 10, 64)
|
||||
isDeployKey, _ := strconv.ParseBool(os.Getenv(models.EnvIsDeployKey))
|
||||
|
||||
hookOptions := private.HookOptions{
|
||||
UserID: userID,
|
||||
@@ -193,8 +179,8 @@ Gitea or set your environment appropriately.`, "")
|
||||
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
||||
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
||||
GitPushOptions: pushOptions(),
|
||||
PullRequestID: prID,
|
||||
DeployKeyID: deployKeyID,
|
||||
ProtectedBranchID: prID,
|
||||
IsDeployKey: isDeployKey,
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
@@ -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,28 +285,26 @@ func runHookUpdate(c *cli.Context) error {
|
||||
}
|
||||
|
||||
func runHookPostReceive(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setup("hooks/post-receive.log", c.Bool("debug"))
|
||||
|
||||
// First of all run update-server-info no matter what
|
||||
if _, _, err := git.NewCommand(ctx, "update-server-info").RunStdString(nil); err != nil {
|
||||
if _, err := git.NewCommand("update-server-info").Run(); err != nil {
|
||||
return fmt.Errorf("Failed to call 'git update-server-info': %v", err)
|
||||
}
|
||||
|
||||
// Now if we're an internal don't do anything else
|
||||
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
|
||||
if os.Getenv(models.EnvIsInternal) == "true" {
|
||||
return nil
|
||||
}
|
||||
|
||||
setup("hooks/post-receive.log", c.Bool("debug"))
|
||||
|
||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
|
||||
return fail(`Rejecting changes as Gitea environment not set.
|
||||
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,12 +320,12 @@ Gitea or set your environment appropriately.`, "")
|
||||
}
|
||||
}
|
||||
|
||||
// the environment is set by serv command
|
||||
repoUser := os.Getenv(repo_module.EnvRepoUsername)
|
||||
isWiki, _ := strconv.ParseBool(os.Getenv(repo_module.EnvRepoIsWiki))
|
||||
repoName := os.Getenv(repo_module.EnvRepoName)
|
||||
pusherID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
|
||||
pusherName := os.Getenv(repo_module.EnvPusherName)
|
||||
// the environment setted on serv command
|
||||
repoUser := os.Getenv(models.EnvRepoUsername)
|
||||
isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
|
||||
repoName := os.Getenv(models.EnvRepoName)
|
||||
pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||
pusherName := os.Getenv(models.EnvPusherName)
|
||||
|
||||
hookOptions := private.HookOptions{
|
||||
UserName: pusherName,
|
||||
@@ -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(repo_module.EnvRepoUsername)
|
||||
repoName := os.Getenv(repo_module.EnvRepoName)
|
||||
pusherID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
|
||||
pusherName := os.Getenv(repo_module.EnvPusherName)
|
||||
|
||||
// 1. Version and features negotiation.
|
||||
// S: PKT-LINE(version=1\0push-options atomic...) / PKT-LINE(version=1\n)
|
||||
// S: flush-pkt
|
||||
// H: PKT-LINE(version=1\0push-options...)
|
||||
// H: flush-pkt
|
||||
|
||||
rs, err := readPktLine(reader, pktLineTypeData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
const VersionHead string = "version=1"
|
||||
|
||||
var (
|
||||
hasPushOptions bool
|
||||
response = []byte(VersionHead)
|
||||
requestOptions []string
|
||||
)
|
||||
|
||||
index := bytes.IndexByte(rs.Data, byte(0))
|
||||
if index >= len(rs.Data) {
|
||||
return fail("Internal Server Error", "pkt-line: format error "+fmt.Sprint(rs.Data))
|
||||
}
|
||||
|
||||
if index < 0 {
|
||||
if len(rs.Data) == 10 && rs.Data[9] == '\n' {
|
||||
index = 9
|
||||
} else {
|
||||
return fail("Internal Server Error", "pkt-line: format error "+fmt.Sprint(rs.Data))
|
||||
}
|
||||
}
|
||||
|
||||
if string(rs.Data[0:index]) != VersionHead {
|
||||
return fail("Internal Server Error", "Received unsupported version: %s", string(rs.Data[0:index]))
|
||||
}
|
||||
requestOptions = strings.Split(string(rs.Data[index+1:]), " ")
|
||||
|
||||
for _, option := range requestOptions {
|
||||
if strings.HasPrefix(option, "push-options") {
|
||||
response = append(response, byte(0))
|
||||
response = append(response, []byte("push-options")...)
|
||||
hasPushOptions = true
|
||||
}
|
||||
}
|
||||
response = append(response, '\n')
|
||||
|
||||
_, err = readPktLine(reader, pktLineTypeFlush)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = writeDataPktLine(os.Stdout, response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = writeFlushPktLine(os.Stdout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. receive commands from server.
|
||||
// S: PKT-LINE(<old-oid> <new-oid> <ref>)
|
||||
// S: ... ...
|
||||
// S: flush-pkt
|
||||
// # [receive push-options]
|
||||
// S: PKT-LINE(push-option)
|
||||
// S: ... ...
|
||||
// S: flush-pkt
|
||||
hookOptions := private.HookOptions{
|
||||
UserName: pusherName,
|
||||
UserID: pusherID,
|
||||
}
|
||||
hookOptions.OldCommitIDs = make([]string, 0, hookBatchSize)
|
||||
hookOptions.NewCommitIDs = make([]string, 0, hookBatchSize)
|
||||
hookOptions.RefFullNames = make([]string, 0, hookBatchSize)
|
||||
|
||||
for {
|
||||
// note: pktLineTypeUnknow means pktLineTypeFlush and pktLineTypeData all allowed
|
||||
rs, err = readPktLine(reader, pktLineTypeUnknow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rs.Type == pktLineTypeFlush {
|
||||
break
|
||||
}
|
||||
t := strings.SplitN(string(rs.Data), " ", 3)
|
||||
if len(t) != 3 {
|
||||
continue
|
||||
}
|
||||
hookOptions.OldCommitIDs = append(hookOptions.OldCommitIDs, t[0])
|
||||
hookOptions.NewCommitIDs = append(hookOptions.NewCommitIDs, t[1])
|
||||
hookOptions.RefFullNames = append(hookOptions.RefFullNames, t[2])
|
||||
}
|
||||
|
||||
hookOptions.GitPushOptions = make(map[string]string)
|
||||
|
||||
if hasPushOptions {
|
||||
for {
|
||||
rs, err = readPktLine(reader, pktLineTypeUnknow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rs.Type == pktLineTypeFlush {
|
||||
break
|
||||
}
|
||||
|
||||
kv := strings.SplitN(string(rs.Data), "=", 2)
|
||||
if len(kv) == 2 {
|
||||
hookOptions.GitPushOptions[kv[0]] = kv[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. run hook
|
||||
resp, err := private.HookProcReceive(ctx, repoUser, repoName, hookOptions)
|
||||
if err != nil {
|
||||
return fail("Internal Server Error", "run proc-receive hook failed :%v", err)
|
||||
}
|
||||
|
||||
// 4. response result to service
|
||||
// # a. OK, but has an alternate reference. The alternate reference name
|
||||
// # and other status can be given in option directives.
|
||||
// H: PKT-LINE(ok <ref>)
|
||||
// H: PKT-LINE(option refname <refname>)
|
||||
// H: PKT-LINE(option old-oid <old-oid>)
|
||||
// H: PKT-LINE(option new-oid <new-oid>)
|
||||
// H: PKT-LINE(option forced-update)
|
||||
// H: ... ...
|
||||
// H: flush-pkt
|
||||
// # b. NO, I reject it.
|
||||
// H: PKT-LINE(ng <ref> <reason>)
|
||||
// # c. Fall through, let 'receive-pack' to execute it.
|
||||
// H: PKT-LINE(ok <ref>)
|
||||
// H: PKT-LINE(option fall-through)
|
||||
|
||||
for _, rs := range resp.Results {
|
||||
if len(rs.Err) > 0 {
|
||||
err = writeDataPktLine(os.Stdout, []byte("ng "+rs.OriginalRef+" "+rs.Err))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if rs.IsNotMatched {
|
||||
err = writeDataPktLine(os.Stdout, []byte("ok "+rs.OriginalRef))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = writeDataPktLine(os.Stdout, []byte("option fall-through"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
err = writeDataPktLine(os.Stdout, []byte("ok "+rs.OriginalRef))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = writeDataPktLine(os.Stdout, []byte("option refname "+rs.Ref))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rs.OldOID != git.EmptySHA {
|
||||
err = writeDataPktLine(os.Stdout, []byte("option old-oid "+rs.OldOID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = writeDataPktLine(os.Stdout, []byte("option new-oid "+rs.NewOID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rs.IsForcePush {
|
||||
err = writeDataPktLine(os.Stdout, []byte("option forced-update"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
err = writeFlushPktLine(os.Stdout)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// git PKT-Line api
|
||||
// pktLineType message type of pkt-line
|
||||
type pktLineType int64
|
||||
|
||||
const (
|
||||
// UnKnow type
|
||||
pktLineTypeUnknow pktLineType = 0
|
||||
// flush-pkt "0000"
|
||||
pktLineTypeFlush pktLineType = iota
|
||||
// data line
|
||||
pktLineTypeData
|
||||
)
|
||||
|
||||
// gitPktLine pkt-line api
|
||||
type gitPktLine struct {
|
||||
Type pktLineType
|
||||
Length uint64
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func readPktLine(in *bufio.Reader, requestType pktLineType) (*gitPktLine, error) {
|
||||
var (
|
||||
err error
|
||||
r *gitPktLine
|
||||
)
|
||||
|
||||
// read prefix
|
||||
lengthBytes := make([]byte, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
lengthBytes[i], err = in.ReadByte()
|
||||
if err != nil {
|
||||
return nil, fail("Internal Server Error", "Pkt-Line: read stdin failed : %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
r = new(gitPktLine)
|
||||
r.Length, err = strconv.ParseUint(string(lengthBytes), 16, 32)
|
||||
if err != nil {
|
||||
return nil, fail("Internal Server Error", "Pkt-Line format is wrong :%v", err)
|
||||
}
|
||||
|
||||
if r.Length == 0 {
|
||||
if requestType == pktLineTypeData {
|
||||
return nil, fail("Internal Server Error", "Pkt-Line format is wrong")
|
||||
}
|
||||
r.Type = pktLineTypeFlush
|
||||
return r, nil
|
||||
}
|
||||
|
||||
if r.Length <= 4 || r.Length > 65520 || requestType == pktLineTypeFlush {
|
||||
return nil, fail("Internal Server Error", "Pkt-Line format is wrong")
|
||||
}
|
||||
|
||||
r.Data = make([]byte, r.Length-4)
|
||||
for i := range r.Data {
|
||||
r.Data[i], err = in.ReadByte()
|
||||
if err != nil {
|
||||
return nil, fail("Internal Server Error", "Pkt-Line: read stdin failed : %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
r.Type = pktLineTypeData
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func writeFlushPktLine(out io.Writer) error {
|
||||
l, err := out.Write([]byte("0000"))
|
||||
if err != nil {
|
||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
||||
}
|
||||
if l != 4 {
|
||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeDataPktLine(out io.Writer, data []byte) error {
|
||||
hexchar := []byte("0123456789abcdef")
|
||||
hex := func(n uint64) byte {
|
||||
return hexchar[(n)&15]
|
||||
}
|
||||
|
||||
length := uint64(len(data) + 4)
|
||||
tmp := make([]byte, 4)
|
||||
tmp[0] = hex(length >> 12)
|
||||
tmp[1] = hex(length >> 8)
|
||||
tmp[2] = hex(length >> 4)
|
||||
tmp[3] = hex(length)
|
||||
|
||||
lr, err := out.Write(tmp)
|
||||
if err != nil {
|
||||
return fail("Internal Server Error", "Pkt-Line response failed: %v", err)
|
||||
}
|
||||
if 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
|
||||
}
|
||||
|
@@ -10,15 +10,11 @@ import (
|
||||
|
||||
"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()
|
||||
setting.NewContext()
|
||||
|
||||
if err := argsSet(c, "title"); err != nil {
|
||||
return err
|
||||
@@ -43,7 +39,7 @@ func runSendMail(c *cli.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
status, message := private.SendEmail(ctx, subject, body, nil)
|
||||
status, message := private.SendEmail(subject, body, nil)
|
||||
if status != http.StatusOK {
|
||||
fmt.Printf("error: %s\n", message)
|
||||
return nil
|
||||
|
@@ -1,23 +0,0 @@
|
||||
// Copyright 2022 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 (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
func init() {
|
||||
setting.SetCustomPathAndConf("", "", "")
|
||||
setting.LoadForTest()
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.MainTest(m, &unittest.TestOptions{
|
||||
GiteaRootPath: "..",
|
||||
})
|
||||
}
|
398
cmd/manager.go
398
cmd/manager.go
@@ -10,6 +10,7 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
@@ -26,7 +27,6 @@ var (
|
||||
subcmdRestart,
|
||||
subcmdFlushQueues,
|
||||
subcmdLogging,
|
||||
subCmdProcesses,
|
||||
},
|
||||
}
|
||||
subcmdShutdown = cli.Command{
|
||||
@@ -58,8 +58,7 @@ var (
|
||||
Name: "timeout",
|
||||
Value: 60 * time.Second,
|
||||
Usage: "Timeout for the flushing process",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
}, cli.BoolFlag{
|
||||
Name: "non-blocking",
|
||||
Usage: "Set to true to not wait for flush to complete before returning",
|
||||
},
|
||||
@@ -68,47 +67,326 @@ var (
|
||||
},
|
||||
},
|
||||
}
|
||||
subCmdProcesses = cli.Command{
|
||||
Name: "processes",
|
||||
Usage: "Display running processes within the current process",
|
||||
Action: runProcesses,
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "flat",
|
||||
Usage: "Show processes as flat table rather than as tree",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "no-system",
|
||||
Usage: "Do not show system proceses",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "stacktraces",
|
||||
Usage: "Show stacktraces",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "json",
|
||||
Usage: "Output as json",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "cancel",
|
||||
Usage: "Process PID to cancel. (Only available for non-system processes.)",
|
||||
defaultLoggingFlags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "group, g",
|
||||
Usage: "Group to add logger to - will default to \"default\"",
|
||||
}, cli.StringFlag{
|
||||
Name: "name, n",
|
||||
Usage: "Name of the new logger - will default to mode",
|
||||
}, cli.StringFlag{
|
||||
Name: "level, l",
|
||||
Usage: "Logging level for the new logger",
|
||||
}, cli.StringFlag{
|
||||
Name: "stacktrace-level, L",
|
||||
Usage: "Stacktrace logging level",
|
||||
}, cli.StringFlag{
|
||||
Name: "flags, F",
|
||||
Usage: "Flags for the logger",
|
||||
}, cli.StringFlag{
|
||||
Name: "expression, e",
|
||||
Usage: "Matching expression for the logger",
|
||||
}, cli.StringFlag{
|
||||
Name: "prefix, p",
|
||||
Usage: "Prefix for the logger",
|
||||
}, cli.BoolFlag{
|
||||
Name: "color",
|
||||
Usage: "Use color in the logs",
|
||||
}, cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
}
|
||||
subcmdLogging = cli.Command{
|
||||
Name: "logging",
|
||||
Usage: "Adjust logging commands",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "pause",
|
||||
Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
Action: runPauseLogging,
|
||||
}, {
|
||||
Name: "resume",
|
||||
Usage: "Resume logging",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
Action: runResumeLogging,
|
||||
}, {
|
||||
Name: "release-and-reopen",
|
||||
Usage: "Cause Gitea to release and re-open files used for logging",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
Action: runReleaseReopenLogging,
|
||||
}, {
|
||||
Name: "remove",
|
||||
Usage: "Remove a logger",
|
||||
ArgsUsage: "[name] Name of logger to remove",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
}, cli.StringFlag{
|
||||
Name: "group, g",
|
||||
Usage: "Group to add logger to - will default to \"default\"",
|
||||
},
|
||||
},
|
||||
Action: runRemoveLogger,
|
||||
}, {
|
||||
Name: "add",
|
||||
Usage: "Add a logger",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "console",
|
||||
Usage: "Add a console logger",
|
||||
Flags: append(defaultLoggingFlags,
|
||||
cli.BoolFlag{
|
||||
Name: "stderr",
|
||||
Usage: "Output console logs to stderr - only relevant for console",
|
||||
}),
|
||||
Action: runAddConsoleLogger,
|
||||
}, {
|
||||
Name: "file",
|
||||
Usage: "Add a file logger",
|
||||
Flags: append(defaultLoggingFlags, []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "filename, f",
|
||||
Usage: "Filename for the logger - this must be set.",
|
||||
}, cli.BoolTFlag{
|
||||
Name: "rotate, r",
|
||||
Usage: "Rotate logs",
|
||||
}, cli.Int64Flag{
|
||||
Name: "max-size, s",
|
||||
Usage: "Maximum size in bytes before rotation",
|
||||
}, cli.BoolTFlag{
|
||||
Name: "daily, d",
|
||||
Usage: "Rotate logs daily",
|
||||
}, cli.IntFlag{
|
||||
Name: "max-days, D",
|
||||
Usage: "Maximum number of daily logs to keep",
|
||||
}, cli.BoolTFlag{
|
||||
Name: "compress, z",
|
||||
Usage: "Compress rotated logs",
|
||||
}, cli.IntFlag{
|
||||
Name: "compression-level, Z",
|
||||
Usage: "Compression level to use",
|
||||
},
|
||||
}...),
|
||||
Action: runAddFileLogger,
|
||||
}, {
|
||||
Name: "conn",
|
||||
Usage: "Add a net conn logger",
|
||||
Flags: append(defaultLoggingFlags, []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "reconnect-on-message, R",
|
||||
Usage: "Reconnect to host for every message",
|
||||
}, cli.BoolFlag{
|
||||
Name: "reconnect, r",
|
||||
Usage: "Reconnect to host when connection is dropped",
|
||||
}, cli.StringFlag{
|
||||
Name: "protocol, P",
|
||||
Usage: "Set protocol to use: tcp, unix, or udp (defaults to tcp)",
|
||||
}, cli.StringFlag{
|
||||
Name: "address, a",
|
||||
Usage: "Host address and port to connect to (defaults to :7020)",
|
||||
},
|
||||
}...),
|
||||
Action: runAddConnLogger,
|
||||
}, {
|
||||
Name: "smtp",
|
||||
Usage: "Add an SMTP logger",
|
||||
Flags: append(defaultLoggingFlags, []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "username, u",
|
||||
Usage: "Mail server username",
|
||||
}, cli.StringFlag{
|
||||
Name: "password, P",
|
||||
Usage: "Mail server password",
|
||||
}, cli.StringFlag{
|
||||
Name: "host, H",
|
||||
Usage: "Mail server host (defaults to: 127.0.0.1:25)",
|
||||
}, cli.StringSliceFlag{
|
||||
Name: "send-to, s",
|
||||
Usage: "Email address(es) to send to",
|
||||
}, cli.StringFlag{
|
||||
Name: "subject, S",
|
||||
Usage: "Subject header of sent emails",
|
||||
},
|
||||
}...),
|
||||
Action: runAddSMTPLogger,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func runShutdown(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
func runRemoveLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.Shutdown(ctx)
|
||||
group := c.String("group")
|
||||
if len(group) == 0 {
|
||||
group = log.DEFAULT
|
||||
}
|
||||
name := c.Args().First()
|
||||
statusCode, msg := private.RemoveLogger(group, name)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runAddSMTPLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
mode := "smtp"
|
||||
if c.IsSet("host") {
|
||||
vals["host"] = c.String("host")
|
||||
} else {
|
||||
vals["host"] = "127.0.0.1:25"
|
||||
}
|
||||
|
||||
if c.IsSet("username") {
|
||||
vals["username"] = c.String("username")
|
||||
}
|
||||
if c.IsSet("password") {
|
||||
vals["password"] = c.String("password")
|
||||
}
|
||||
|
||||
if !c.IsSet("send-to") {
|
||||
return fmt.Errorf("Some recipients must be provided")
|
||||
}
|
||||
vals["sendTos"] = c.StringSlice("send-to")
|
||||
|
||||
if c.IsSet("subject") {
|
||||
vals["subject"] = c.String("subject")
|
||||
} else {
|
||||
vals["subject"] = "Diagnostic message from Gitea"
|
||||
}
|
||||
|
||||
return commonAddLogger(c, mode, vals)
|
||||
}
|
||||
|
||||
func runAddConnLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
mode := "conn"
|
||||
vals["net"] = "tcp"
|
||||
if c.IsSet("protocol") {
|
||||
switch c.String("protocol") {
|
||||
case "udp":
|
||||
vals["net"] = "udp"
|
||||
case "unix":
|
||||
vals["net"] = "unix"
|
||||
}
|
||||
}
|
||||
if c.IsSet("address") {
|
||||
vals["address"] = c.String("address")
|
||||
} else {
|
||||
vals["address"] = ":7020"
|
||||
}
|
||||
if c.IsSet("reconnect") {
|
||||
vals["reconnect"] = c.Bool("reconnect")
|
||||
}
|
||||
if c.IsSet("reconnect-on-message") {
|
||||
vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
|
||||
}
|
||||
return commonAddLogger(c, mode, vals)
|
||||
}
|
||||
|
||||
func runAddFileLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
mode := "file"
|
||||
if c.IsSet("filename") {
|
||||
vals["filename"] = c.String("filename")
|
||||
} else {
|
||||
return fmt.Errorf("filename must be set when creating a file logger")
|
||||
}
|
||||
if c.IsSet("rotate") {
|
||||
vals["rotate"] = c.Bool("rotate")
|
||||
}
|
||||
if c.IsSet("max-size") {
|
||||
vals["maxsize"] = c.Int64("max-size")
|
||||
}
|
||||
if c.IsSet("daily") {
|
||||
vals["daily"] = c.Bool("daily")
|
||||
}
|
||||
if c.IsSet("max-days") {
|
||||
vals["maxdays"] = c.Int("max-days")
|
||||
}
|
||||
if c.IsSet("compress") {
|
||||
vals["compress"] = c.Bool("compress")
|
||||
}
|
||||
if c.IsSet("compression-level") {
|
||||
vals["compressionLevel"] = c.Int("compression-level")
|
||||
}
|
||||
return commonAddLogger(c, mode, vals)
|
||||
}
|
||||
|
||||
func runAddConsoleLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
mode := "console"
|
||||
if c.IsSet("stderr") && c.Bool("stderr") {
|
||||
vals["stderr"] = c.Bool("stderr")
|
||||
}
|
||||
return commonAddLogger(c, mode, vals)
|
||||
}
|
||||
|
||||
func commonAddLogger(c *cli.Context, mode string, vals map[string]interface{}) error {
|
||||
if len(c.String("level")) > 0 {
|
||||
vals["level"] = log.FromString(c.String("level")).String()
|
||||
}
|
||||
if len(c.String("stacktrace-level")) > 0 {
|
||||
vals["stacktraceLevel"] = log.FromString(c.String("stacktrace-level")).String()
|
||||
}
|
||||
if len(c.String("expression")) > 0 {
|
||||
vals["expression"] = c.String("expression")
|
||||
}
|
||||
if len(c.String("prefix")) > 0 {
|
||||
vals["prefix"] = c.String("prefix")
|
||||
}
|
||||
if len(c.String("flags")) > 0 {
|
||||
vals["flags"] = log.FlagsFromString(c.String("flags"))
|
||||
}
|
||||
if c.IsSet("color") {
|
||||
vals["colorize"] = c.Bool("color")
|
||||
}
|
||||
group := "default"
|
||||
if c.IsSet("group") {
|
||||
group = c.String("group")
|
||||
}
|
||||
name := mode
|
||||
if c.IsSet("name") {
|
||||
name = c.String("name")
|
||||
}
|
||||
statusCode, msg := private.AddLogger(group, name, mode, vals)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runShutdown(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.Shutdown()
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
@@ -116,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)
|
||||
@@ -131,30 +406,49 @@ 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)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runProcesses(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
func runPauseLogging(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.Processes(ctx, os.Stdout, c.Bool("flat"), c.Bool("no-system"), c.Bool("stacktraces"), c.Bool("json"), c.String("cancel"))
|
||||
statusCode, msg := private.PauseLogging()
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runResumeLogging(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.ResumeLogging()
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runReleaseReopenLogging(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.ReleaseReopenLogging()
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
@@ -1,383 +0,0 @@
|
||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultLoggingFlags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "group, g",
|
||||
Usage: "Group to add logger to - will default to \"default\"",
|
||||
}, cli.StringFlag{
|
||||
Name: "name, n",
|
||||
Usage: "Name of the new logger - will default to mode",
|
||||
}, cli.StringFlag{
|
||||
Name: "level, l",
|
||||
Usage: "Logging level for the new logger",
|
||||
}, cli.StringFlag{
|
||||
Name: "stacktrace-level, L",
|
||||
Usage: "Stacktrace logging level",
|
||||
}, cli.StringFlag{
|
||||
Name: "flags, F",
|
||||
Usage: "Flags for the logger",
|
||||
}, cli.StringFlag{
|
||||
Name: "expression, e",
|
||||
Usage: "Matching expression for the logger",
|
||||
}, cli.StringFlag{
|
||||
Name: "prefix, p",
|
||||
Usage: "Prefix for the logger",
|
||||
}, cli.BoolFlag{
|
||||
Name: "color",
|
||||
Usage: "Use color in the logs",
|
||||
}, cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
}
|
||||
|
||||
subcmdLogging = cli.Command{
|
||||
Name: "logging",
|
||||
Usage: "Adjust logging commands",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "pause",
|
||||
Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
Action: runPauseLogging,
|
||||
}, {
|
||||
Name: "resume",
|
||||
Usage: "Resume logging",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
Action: runResumeLogging,
|
||||
}, {
|
||||
Name: "release-and-reopen",
|
||||
Usage: "Cause Gitea to release and re-open files used for logging",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
},
|
||||
},
|
||||
Action: runReleaseReopenLogging,
|
||||
}, {
|
||||
Name: "remove",
|
||||
Usage: "Remove a logger",
|
||||
ArgsUsage: "[name] Name of logger to remove",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
}, cli.StringFlag{
|
||||
Name: "group, g",
|
||||
Usage: "Group to add logger to - will default to \"default\"",
|
||||
},
|
||||
},
|
||||
Action: runRemoveLogger,
|
||||
}, {
|
||||
Name: "add",
|
||||
Usage: "Add a logger",
|
||||
Subcommands: []cli.Command{
|
||||
{
|
||||
Name: "console",
|
||||
Usage: "Add a console logger",
|
||||
Flags: append(defaultLoggingFlags,
|
||||
cli.BoolFlag{
|
||||
Name: "stderr",
|
||||
Usage: "Output console logs to stderr - only relevant for console",
|
||||
}),
|
||||
Action: runAddConsoleLogger,
|
||||
}, {
|
||||
Name: "file",
|
||||
Usage: "Add a file logger",
|
||||
Flags: append(defaultLoggingFlags, []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "filename, f",
|
||||
Usage: "Filename for the logger - this must be set.",
|
||||
}, cli.BoolTFlag{
|
||||
Name: "rotate, r",
|
||||
Usage: "Rotate logs",
|
||||
}, cli.Int64Flag{
|
||||
Name: "max-size, s",
|
||||
Usage: "Maximum size in bytes before rotation",
|
||||
}, cli.BoolTFlag{
|
||||
Name: "daily, d",
|
||||
Usage: "Rotate logs daily",
|
||||
}, cli.IntFlag{
|
||||
Name: "max-days, D",
|
||||
Usage: "Maximum number of daily logs to keep",
|
||||
}, cli.BoolTFlag{
|
||||
Name: "compress, z",
|
||||
Usage: "Compress rotated logs",
|
||||
}, cli.IntFlag{
|
||||
Name: "compression-level, Z",
|
||||
Usage: "Compression level to use",
|
||||
},
|
||||
}...),
|
||||
Action: runAddFileLogger,
|
||||
}, {
|
||||
Name: "conn",
|
||||
Usage: "Add a net conn logger",
|
||||
Flags: append(defaultLoggingFlags, []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "reconnect-on-message, R",
|
||||
Usage: "Reconnect to host for every message",
|
||||
}, cli.BoolFlag{
|
||||
Name: "reconnect, r",
|
||||
Usage: "Reconnect to host when connection is dropped",
|
||||
}, cli.StringFlag{
|
||||
Name: "protocol, P",
|
||||
Usage: "Set protocol to use: tcp, unix, or udp (defaults to tcp)",
|
||||
}, cli.StringFlag{
|
||||
Name: "address, a",
|
||||
Usage: "Host address and port to connect to (defaults to :7020)",
|
||||
},
|
||||
}...),
|
||||
Action: runAddConnLogger,
|
||||
}, {
|
||||
Name: "smtp",
|
||||
Usage: "Add an SMTP logger",
|
||||
Flags: append(defaultLoggingFlags, []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "username, u",
|
||||
Usage: "Mail server username",
|
||||
}, cli.StringFlag{
|
||||
Name: "password, P",
|
||||
Usage: "Mail server password",
|
||||
}, cli.StringFlag{
|
||||
Name: "host, H",
|
||||
Usage: "Mail server host (defaults to: 127.0.0.1:25)",
|
||||
}, cli.StringSliceFlag{
|
||||
Name: "send-to, s",
|
||||
Usage: "Email address(es) to send to",
|
||||
}, cli.StringFlag{
|
||||
Name: "subject, S",
|
||||
Usage: "Subject header of sent emails",
|
||||
},
|
||||
}...),
|
||||
Action: runAddSMTPLogger,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func runRemoveLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
group := c.String("group")
|
||||
if len(group) == 0 {
|
||||
group = log.DEFAULT
|
||||
}
|
||||
name := c.Args().First()
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
statusCode, msg := private.RemoveLogger(ctx, group, name)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runAddSMTPLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
mode := "smtp"
|
||||
if c.IsSet("host") {
|
||||
vals["host"] = c.String("host")
|
||||
} else {
|
||||
vals["host"] = "127.0.0.1:25"
|
||||
}
|
||||
|
||||
if c.IsSet("username") {
|
||||
vals["username"] = c.String("username")
|
||||
}
|
||||
if c.IsSet("password") {
|
||||
vals["password"] = c.String("password")
|
||||
}
|
||||
|
||||
if !c.IsSet("send-to") {
|
||||
return fmt.Errorf("Some recipients must be provided")
|
||||
}
|
||||
vals["sendTos"] = c.StringSlice("send-to")
|
||||
|
||||
if c.IsSet("subject") {
|
||||
vals["subject"] = c.String("subject")
|
||||
} else {
|
||||
vals["subject"] = "Diagnostic message from Gitea"
|
||||
}
|
||||
|
||||
return commonAddLogger(c, mode, vals)
|
||||
}
|
||||
|
||||
func runAddConnLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
mode := "conn"
|
||||
vals["net"] = "tcp"
|
||||
if c.IsSet("protocol") {
|
||||
switch c.String("protocol") {
|
||||
case "udp":
|
||||
vals["net"] = "udp"
|
||||
case "unix":
|
||||
vals["net"] = "unix"
|
||||
}
|
||||
}
|
||||
if c.IsSet("address") {
|
||||
vals["address"] = c.String("address")
|
||||
} else {
|
||||
vals["address"] = ":7020"
|
||||
}
|
||||
if c.IsSet("reconnect") {
|
||||
vals["reconnect"] = c.Bool("reconnect")
|
||||
}
|
||||
if c.IsSet("reconnect-on-message") {
|
||||
vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
|
||||
}
|
||||
return commonAddLogger(c, mode, vals)
|
||||
}
|
||||
|
||||
func runAddFileLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
mode := "file"
|
||||
if c.IsSet("filename") {
|
||||
vals["filename"] = c.String("filename")
|
||||
} else {
|
||||
return fmt.Errorf("filename must be set when creating a file logger")
|
||||
}
|
||||
if c.IsSet("rotate") {
|
||||
vals["rotate"] = c.Bool("rotate")
|
||||
}
|
||||
if c.IsSet("max-size") {
|
||||
vals["maxsize"] = c.Int64("max-size")
|
||||
}
|
||||
if c.IsSet("daily") {
|
||||
vals["daily"] = c.Bool("daily")
|
||||
}
|
||||
if c.IsSet("max-days") {
|
||||
vals["maxdays"] = c.Int("max-days")
|
||||
}
|
||||
if c.IsSet("compress") {
|
||||
vals["compress"] = c.Bool("compress")
|
||||
}
|
||||
if c.IsSet("compression-level") {
|
||||
vals["compressionLevel"] = c.Int("compression-level")
|
||||
}
|
||||
return commonAddLogger(c, mode, vals)
|
||||
}
|
||||
|
||||
func runAddConsoleLogger(c *cli.Context) error {
|
||||
setup("manager", c.Bool("debug"))
|
||||
vals := map[string]interface{}{}
|
||||
mode := "console"
|
||||
if c.IsSet("stderr") && c.Bool("stderr") {
|
||||
vals["stderr"] = c.Bool("stderr")
|
||||
}
|
||||
return commonAddLogger(c, mode, vals)
|
||||
}
|
||||
|
||||
func commonAddLogger(c *cli.Context, mode string, vals map[string]interface{}) error {
|
||||
if len(c.String("level")) > 0 {
|
||||
vals["level"] = log.FromString(c.String("level")).String()
|
||||
}
|
||||
if len(c.String("stacktrace-level")) > 0 {
|
||||
vals["stacktraceLevel"] = log.FromString(c.String("stacktrace-level")).String()
|
||||
}
|
||||
if len(c.String("expression")) > 0 {
|
||||
vals["expression"] = c.String("expression")
|
||||
}
|
||||
if len(c.String("prefix")) > 0 {
|
||||
vals["prefix"] = c.String("prefix")
|
||||
}
|
||||
if len(c.String("flags")) > 0 {
|
||||
vals["flags"] = log.FlagsFromString(c.String("flags"))
|
||||
}
|
||||
if c.IsSet("color") {
|
||||
vals["colorize"] = c.Bool("color")
|
||||
}
|
||||
group := "default"
|
||||
if c.IsSet("group") {
|
||||
group = c.String("group")
|
||||
}
|
||||
name := mode
|
||||
if c.IsSet("name") {
|
||||
name = c.String("name")
|
||||
}
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
statusCode, msg := private.AddLogger(ctx, group, name, mode, vals)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runPauseLogging(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.PauseLogging(ctx)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runResumeLogging(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.ResumeLogging(ctx)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runReleaseReopenLogging(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setup("manager", c.Bool("debug"))
|
||||
statusCode, msg := private.ReleaseReopenLogging(ctx)
|
||||
switch statusCode {
|
||||
case http.StatusInternalServerError:
|
||||
return fail("InternalServerError", msg)
|
||||
}
|
||||
|
||||
fmt.Fprintln(os.Stdout, msg)
|
||||
return nil
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
@@ -9,14 +9,9 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/models/migrations"
|
||||
packages_model "code.gitea.io/gitea/models/packages"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
packages_module "code.gitea.io/gitea/modules/packages"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
|
||||
@@ -27,13 +22,13 @@ import (
|
||||
var CmdMigrateStorage = cli.Command{
|
||||
Name: "migrate-storage",
|
||||
Usage: "Migrate the storage",
|
||||
Description: "Copies stored files from storage configured in app.ini to parameter-configured storage",
|
||||
Description: "This is a command for migrating storage.",
|
||||
Action: runMigrateStorage,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "type, t",
|
||||
Value: "",
|
||||
Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages'",
|
||||
Usage: "Kinds of files to migrate, currently only 'attachments' is supported",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "storage, s",
|
||||
@@ -82,69 +77,52 @@ var CmdMigrateStorage = cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
func migrateAttachments(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||
return db.IterateObjects(ctx, func(attach *repo_model.Attachment) error {
|
||||
func migrateAttachments(dstStorage storage.ObjectStorage) error {
|
||||
return models.IterateAttachment(func(attach *models.Attachment) error {
|
||||
_, err := storage.Copy(dstStorage, attach.RelativePath(), storage.Attachments, attach.RelativePath())
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func migrateLFS(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||
return db.IterateObjects(ctx, func(mo *git_model.LFSMetaObject) error {
|
||||
func migrateLFS(dstStorage storage.ObjectStorage) error {
|
||||
return models.IterateLFS(func(mo *models.LFSMetaObject) error {
|
||||
_, err := storage.Copy(dstStorage, mo.RelativePath(), storage.LFS, mo.RelativePath())
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func migrateAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||
return db.IterateObjects(ctx, func(user *user_model.User) error {
|
||||
func migrateAvatars(dstStorage storage.ObjectStorage) error {
|
||||
return models.IterateUser(func(user *models.User) error {
|
||||
_, err := storage.Copy(dstStorage, user.CustomAvatarRelativePath(), storage.Avatars, user.CustomAvatarRelativePath())
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func migrateRepoAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||
return db.IterateObjects(ctx, func(repo *repo_model.Repository) error {
|
||||
func migrateRepoAvatars(dstStorage storage.ObjectStorage) error {
|
||||
return models.IterateRepository(func(repo *models.Repository) error {
|
||||
_, err := storage.Copy(dstStorage, repo.CustomAvatarRelativePath(), storage.RepoAvatars, repo.CustomAvatarRelativePath())
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func migrateRepoArchivers(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||
return db.IterateObjects(ctx, func(archiver *repo_model.RepoArchiver) error {
|
||||
p := archiver.RelativePath()
|
||||
_, err := storage.Copy(dstStorage, p, storage.RepoArchives, p)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func migratePackages(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||
return db.IterateObjects(ctx, func(pb *packages_model.PackageBlob) error {
|
||||
p := packages_module.KeyToRelativePath(packages_module.BlobHash256Key(pb.HashSHA256))
|
||||
_, err := storage.Copy(dstStorage, p, storage.Packages, p)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func runMigrateStorage(ctx *cli.Context) error {
|
||||
stdCtx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(stdCtx); err != nil {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
goCtx := context.Background()
|
||||
|
||||
if err := storage.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -161,13 +139,13 @@ func runMigrateStorage(ctx *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
dstStorage, err = storage.NewLocalStorage(
|
||||
stdCtx,
|
||||
goCtx,
|
||||
storage.LocalStorageConfig{
|
||||
Path: p,
|
||||
})
|
||||
case string(storage.MinioStorageType):
|
||||
dstStorage, err = storage.NewMinioStorage(
|
||||
stdCtx,
|
||||
goCtx,
|
||||
storage.MinioStorageConfig{
|
||||
Endpoint: ctx.String("minio-endpoint"),
|
||||
AccessKeyID: ctx.String("minio-access-key-id"),
|
||||
@@ -178,29 +156,35 @@ func runMigrateStorage(ctx *cli.Context) error {
|
||||
UseSSL: ctx.Bool("minio-use-ssl"),
|
||||
})
|
||||
default:
|
||||
return fmt.Errorf("unsupported storage type: %s", ctx.String("storage"))
|
||||
return fmt.Errorf("Unsupported storage type: %s", ctx.String("storage"))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
migratedMethods := map[string]func(context.Context, storage.ObjectStorage) error{
|
||||
"attachments": migrateAttachments,
|
||||
"lfs": migrateLFS,
|
||||
"avatars": migrateAvatars,
|
||||
"repo-avatars": migrateRepoAvatars,
|
||||
"repo-archivers": migrateRepoArchivers,
|
||||
"packages": migratePackages,
|
||||
}
|
||||
|
||||
tp := strings.ToLower(ctx.String("type"))
|
||||
if m, ok := migratedMethods[tp]; ok {
|
||||
if err := m(stdCtx, dstStorage); err != nil {
|
||||
switch tp {
|
||||
case "attachments":
|
||||
if err := migrateAttachments(dstStorage); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("%s files have successfully been copied to the new storage.", tp)
|
||||
return nil
|
||||
case "lfs":
|
||||
if err := migrateLFS(dstStorage); err != nil {
|
||||
return err
|
||||
}
|
||||
case "avatars":
|
||||
if err := migrateAvatars(dstStorage); err != nil {
|
||||
return err
|
||||
}
|
||||
case "repo-avatars":
|
||||
if err := migrateRepoAvatars(dstStorage); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("Unsupported storage: %s", ctx.String("type"))
|
||||
}
|
||||
|
||||
return fmt.Errorf("unsupported storage: %s", ctx.String("type"))
|
||||
log.Warn("All files have been copied to the new placement but old files are still on the orignial placement.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -1,74 +0,0 @@
|
||||
// Copyright 2022 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"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/packages"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
packages_module "code.gitea.io/gitea/modules/packages"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
packages_service "code.gitea.io/gitea/services/packages"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMigratePackages(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
creator := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
|
||||
|
||||
content := "package main\n\nfunc main() {\nfmt.Println(\"hi\")\n}\n"
|
||||
buf, err := packages_module.CreateHashedBufferFromReader(strings.NewReader(content), 1024)
|
||||
assert.NoError(t, err)
|
||||
defer buf.Close()
|
||||
|
||||
v, f, err := packages_service.CreatePackageAndAddFile(&packages_service.PackageCreationInfo{
|
||||
PackageInfo: packages_service.PackageInfo{
|
||||
Owner: creator,
|
||||
PackageType: packages.TypeGeneric,
|
||||
Name: "test",
|
||||
Version: "1.0.0",
|
||||
},
|
||||
Creator: creator,
|
||||
SemverCompatible: true,
|
||||
VersionProperties: map[string]string{},
|
||||
}, &packages_service.PackageFileCreationInfo{
|
||||
PackageFileInfo: packages_service.PackageFileInfo{
|
||||
Filename: "a.go",
|
||||
},
|
||||
Data: buf,
|
||||
IsLead: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, v)
|
||||
assert.NotNil(t, f)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
p, err := os.MkdirTemp(os.TempDir(), "migrated_packages")
|
||||
assert.NoError(t, err)
|
||||
|
||||
dstStorage, err := storage.NewLocalStorage(
|
||||
ctx,
|
||||
storage.LocalStorageConfig{
|
||||
Path: p,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = migratePackages(ctx, dstStorage)
|
||||
assert.NoError(t, err)
|
||||
|
||||
entries, err := os.ReadDir(p)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 2, len(entries))
|
||||
assert.EqualValues(t, "01", entries[0].Name())
|
||||
assert.EqualValues(t, "tmp", entries[1].Name())
|
||||
}
|
@@ -5,13 +5,15 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/migrations"
|
||||
"code.gitea.io/gitea/modules/migrations/base"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
@@ -41,37 +43,77 @@ var CmdRestoreRepository = cli.Command{
|
||||
cli.StringFlag{
|
||||
Name: "units",
|
||||
Value: "",
|
||||
Usage: `Which items will be restored, one or more units should be separated as comma.
|
||||
Usage: `Which items will be restored, one or more units should be separated as comma.
|
||||
wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "validation",
|
||||
Usage: "Sanity check the content of the files before trying to load them",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func runRestoreRepository(c *cli.Context) error {
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
setting.LoadFromExisting()
|
||||
var units []string
|
||||
if s := c.String("units"); s != "" {
|
||||
units = strings.Split(s, ",")
|
||||
}
|
||||
statusCode, errStr := private.RestoreRepo(
|
||||
ctx,
|
||||
c.String("repo_dir"),
|
||||
c.String("owner_name"),
|
||||
c.String("repo_name"),
|
||||
units,
|
||||
c.Bool("validation"),
|
||||
)
|
||||
if statusCode == http.StatusOK {
|
||||
return nil
|
||||
func runRestoreRepository(ctx *cli.Context) error {
|
||||
if err := initDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Fatal("Failed to restore repository: %v", errStr)
|
||||
return errors.New(errStr)
|
||||
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 := storage.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := pull_service.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var opts = base.MigrateOptions{
|
||||
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.RestoreRepository(
|
||||
context.Background(),
|
||||
ctx.String("repo_dir"),
|
||||
ctx.String("owner_name"),
|
||||
ctx.String("repo_name"),
|
||||
); err != nil {
|
||||
log.Fatal("Failed to restore repository: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
171
cmd/serv.go
171
cmd/serv.go
@@ -6,7 +6,6 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -17,20 +16,15 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/lfs"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/pprof"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/services/lfs"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/kballard/go-shellquote"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
@@ -62,61 +56,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"
|
||||
}
|
||||
|
||||
// Check if setting.RepoRootPath exists. It could be the case that it doesn't exist, this can happen when
|
||||
// `[repository]` `ROOT` is a relative path and $GITEA_WORK_DIR isn't passed to the SSH connection.
|
||||
if _, err := os.Stat(setting.RepoRootPath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
_ = fail("Incorrect configuration, no repository directory.", "Directory `[repository].ROOT` %q was not found, please check if $GITEA_WORK_DIR is passed to the SSH connection or make `[repository].ROOT` an absolute value.", setting.RepoRootPath)
|
||||
} else {
|
||||
_ = fail("Incorrect configuration, repository directory is inaccessible", "Directory `[repository].ROOT` %q is inaccessible. err: %v", setting.RepoRootPath, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := git.InitSimple(context.Background()); err != nil {
|
||||
_ = fail("Failed to init git", "Failed to init git, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
allowedCommands = map[string]perm.AccessMode{
|
||||
"git-upload-pack": perm.AccessModeRead,
|
||||
"git-upload-archive": perm.AccessModeRead,
|
||||
"git-receive-pack": perm.AccessModeWrite,
|
||||
lfsAuthenticateVerb: perm.AccessModeNone,
|
||||
allowedCommands = map[string]models.AccessMode{
|
||||
"git-upload-pack": models.AccessModeRead,
|
||||
"git-upload-archive": models.AccessModeRead,
|
||||
"git-receive-pack": models.AccessModeWrite,
|
||||
lfsAuthenticateVerb: models.AccessModeNone,
|
||||
}
|
||||
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||
)
|
||||
|
||||
func fail(userMessage, logMessage string, args ...interface{}) error {
|
||||
// There appears to be a chance to cause a zombie process and failure to read the Exit status
|
||||
// if nothing is outputted on stdout.
|
||||
_, _ = fmt.Fprintln(os.Stdout, "")
|
||||
_, _ = fmt.Fprintln(os.Stderr, "Gitea:", userMessage)
|
||||
func fail(userMessage, logMessage string, args ...interface{}) {
|
||||
fmt.Fprintln(os.Stderr, "Gitea:", userMessage)
|
||||
|
||||
if len(logMessage) > 0 {
|
||||
if !setting.IsProd {
|
||||
_, _ = fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
|
||||
if !setting.IsProd() {
|
||||
fmt.Fprintf(os.Stderr, logMessage+"\n", args...)
|
||||
}
|
||||
}
|
||||
ctx, cancel := installSignals()
|
||||
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"))
|
||||
|
||||
@@ -134,23 +102,23 @@ 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])
|
||||
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()[1])
|
||||
}
|
||||
|
||||
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.")
|
||||
@@ -163,18 +131,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]
|
||||
@@ -186,7 +147,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 {
|
||||
@@ -199,70 +160,82 @@ 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)
|
||||
os.Setenv(models.EnvRepoUsername, results.OwnerName)
|
||||
os.Setenv(models.EnvPusherName, results.UserName)
|
||||
os.Setenv(models.EnvPusherEmail, results.UserEmail)
|
||||
os.Setenv(models.EnvPusherID, strconv.FormatInt(results.UserID, 10))
|
||||
os.Setenv(models.EnvRepoID, strconv.FormatInt(results.RepoID, 10))
|
||||
os.Setenv(models.EnvPRID, fmt.Sprintf("%d", 0))
|
||||
os.Setenv(models.EnvIsDeployKey, fmt.Sprintf("%t", results.IsDeployKey))
|
||||
os.Setenv(models.EnvKeyID, fmt.Sprintf("%d", results.KeyID))
|
||||
os.Setenv(models.EnvAppURL, setting.AppURL)
|
||||
|
||||
// LFS token authentication
|
||||
//LFS token authentication
|
||||
if verb == lfsAuthenticateVerb {
|
||||
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
|
||||
|
||||
now := time.Now()
|
||||
claims := lfs.Claims{
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(now.Add(setting.LFS.HTTPAuthExpiry)),
|
||||
NotBefore: jwt.NewNumericDate(now),
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
ExpiresAt: now.Add(setting.LFS.HTTPAuthExpiry).Unix(),
|
||||
NotBefore: now.Unix(),
|
||||
},
|
||||
RepoID: results.RepoID,
|
||||
Op: lfsVerb,
|
||||
@@ -273,19 +246,20 @@ 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 := &git_model.LFSTokenResponse{
|
||||
tokenAuthentication := &models.LFSTokenResponse{
|
||||
Header: make(map[string]string),
|
||||
Href: url,
|
||||
}
|
||||
tokenAuthentication.Header["Authorization"] = fmt.Sprintf("Bearer %s", tokenString)
|
||||
|
||||
json := jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
err = enc.Encode(tokenAuthentication)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
@@ -298,42 +272,23 @@ 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)
|
||||
}
|
||||
|
||||
process.SetSysProcAttribute(gitcmd)
|
||||
gitcmd.Dir = setting.RepoRootPath
|
||||
gitcmd.Stdout = os.Stdout
|
||||
gitcmd.Stdin = os.Stdin
|
||||
gitcmd.Stderr = os.Stderr
|
||||
gitcmd.Env = append(gitcmd.Env, os.Environ()...)
|
||||
gitcmd.Env = append(gitcmd.Env,
|
||||
repo_module.EnvRepoIsWiki+"="+strconv.FormatBool(results.IsWiki),
|
||||
repo_module.EnvRepoName+"="+results.RepoName,
|
||||
repo_module.EnvRepoUsername+"="+results.OwnerName,
|
||||
repo_module.EnvPusherName+"="+results.UserName,
|
||||
repo_module.EnvPusherEmail+"="+results.UserEmail,
|
||||
repo_module.EnvPusherID+"="+strconv.FormatInt(results.UserID, 10),
|
||||
repo_module.EnvRepoID+"="+strconv.FormatInt(results.RepoID, 10),
|
||||
repo_module.EnvPRID+"="+fmt.Sprintf("%d", 0),
|
||||
repo_module.EnvDeployKeyID+"="+fmt.Sprintf("%d", results.DeployKeyID),
|
||||
repo_module.EnvKeyID+"="+fmt.Sprintf("%d", results.KeyID),
|
||||
repo_module.EnvAppURL+"="+setting.AppURL,
|
||||
)
|
||||
// to avoid breaking, here only use the minimal environment variables for the "gitea serv" command.
|
||||
// it could be re-considered whether to use the same git.CommonGitCmdEnvs() as "git" command later.
|
||||
gitcmd.Env = append(gitcmd.Env, git.CommonCmdServEnvs()...)
|
||||
|
||||
if err = gitcmd.Run(); err != nil {
|
||||
return fail("Internal error", "Failed to execute git command: %v", err)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
121
cmd/web.go
121
cmd/web.go
@@ -9,19 +9,18 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
|
||||
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/routers"
|
||||
"code.gitea.io/gitea/routers/install"
|
||||
"code.gitea.io/gitea/routers/routes"
|
||||
|
||||
"github.com/felixge/fgprof"
|
||||
context2 "github.com/gorilla/context"
|
||||
"github.com/urfave/cli"
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
@@ -49,21 +48,10 @@ and it takes care of all the other things for you`,
|
||||
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",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func runHTTPRedirector() {
|
||||
_, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: HTTP Redirector", process.SystemProcessType, true)
|
||||
defer finished()
|
||||
|
||||
source := fmt.Sprintf("%s:%s", setting.HTTPAddr, setting.PortToRedirect)
|
||||
dest := strings.TrimSuffix(setting.AppURL, "/")
|
||||
log.Info("Redirecting: %s to %s", source, dest)
|
||||
@@ -76,26 +64,14 @@ func runHTTPRedirector() {
|
||||
http.Redirect(w, r, target, http.StatusTemporaryRedirect)
|
||||
})
|
||||
|
||||
err := runHTTP("tcp", source, "HTTP Redirector", handler)
|
||||
var err = runHTTP("tcp", source, "HTTP Redirector", context2.ClearHandler(handler))
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("Failed to start port redirection: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func runWeb(ctx *cli.Context) error {
|
||||
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))
|
||||
}
|
||||
defer func() {
|
||||
if panicked := recover(); panicked != nil {
|
||||
log.Fatal("PANIC: %v\n%s", panicked, log.Stack(2))
|
||||
}
|
||||
}()
|
||||
|
||||
managerCtx, cancel := context.WithCancel(context.Background())
|
||||
graceful.InitManager(managerCtx)
|
||||
defer cancel()
|
||||
@@ -113,7 +89,7 @@ func runWeb(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
// Perform pre-initialization
|
||||
needsInstall := install.PreloadSettings(graceful.GetManager().HammerContext())
|
||||
needsInstall := routers.PreInstallInit(graceful.GetManager().HammerContext())
|
||||
if needsInstall {
|
||||
// Flag for port number in case first time run conflict
|
||||
if ctx.IsSet("port") {
|
||||
@@ -126,12 +102,8 @@ func runWeb(ctx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
c := install.Routes()
|
||||
c := routes.InstallRoutes()
|
||||
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()
|
||||
@@ -146,25 +118,14 @@ func runWeb(ctx *cli.Context) error {
|
||||
|
||||
if setting.EnablePprof {
|
||||
go func() {
|
||||
http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler())
|
||||
_, _, finished := process.GetManager().AddTypedContext(context.Background(), "Web: PProf Server", process.SystemProcessType, true)
|
||||
log.Info("Starting pprof server on localhost:6060")
|
||||
log.Info("%v", http.ListenAndServe("localhost:6060", nil))
|
||||
finished()
|
||||
}()
|
||||
}
|
||||
|
||||
log.Info("Global init")
|
||||
// Perform global initialization
|
||||
setting.LoadFromExisting()
|
||||
routers.GlobalInitInstalled(graceful.GetManager().HammerContext())
|
||||
|
||||
// We check that AppDataPath exists here (it should have been created during installation)
|
||||
// We can't check it in `GlobalInitInstalled`, because some integration tests
|
||||
// use cmd -> GlobalInitInstalled, but the AppDataPath doesn't exist during those tests.
|
||||
if _, err := os.Stat(setting.AppDataPath); err != nil {
|
||||
log.Fatal("Can not find APP_DATA_PATH '%s'", setting.AppDataPath)
|
||||
}
|
||||
routers.GlobalInit(graceful.GetManager().HammerContext())
|
||||
|
||||
// Override the provided port number within the configuration
|
||||
if ctx.IsSet("port") {
|
||||
@@ -174,7 +135,7 @@ func runWeb(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
// Set up Chi routes
|
||||
c := routers.NormalRoutes()
|
||||
c := routes.NormalRoutes()
|
||||
err := listen(c, true)
|
||||
<-graceful.GetManager().Done()
|
||||
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
||||
@@ -187,10 +148,23 @@ func setPort(port string) error {
|
||||
setting.HTTPPort = port
|
||||
|
||||
switch setting.Protocol {
|
||||
case setting.HTTPUnix:
|
||||
case setting.UnixSocket:
|
||||
case setting.FCGI:
|
||||
case setting.FCGIUnix:
|
||||
default:
|
||||
// Save LOCAL_ROOT_URL if port changed
|
||||
cfg := ini.Empty()
|
||||
isFile, err := util.IsFile(setting.CustomConf)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to check if %s is a file", err)
|
||||
}
|
||||
if isFile {
|
||||
// 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"
|
||||
@@ -199,26 +173,20 @@ func setPort(port string) error {
|
||||
}
|
||||
defaultLocalURL += ":" + setting.HTTPPort + "/"
|
||||
|
||||
// Save LOCAL_ROOT_URL if port changed
|
||||
setting.CreateOrAppendToCustomConf(func(cfg *ini.File) {
|
||||
cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
|
||||
})
|
||||
cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
|
||||
if err := cfg.SaveTo(setting.CustomConf); err != nil {
|
||||
return fmt.Errorf("Error saving generated JWT Secret to custom config: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func listen(m http.Handler, handleRedirector bool) error {
|
||||
listenAddr := setting.HTTPAddr
|
||||
if setting.Protocol != setting.HTTPUnix && setting.Protocol != setting.FCGIUnix {
|
||||
if setting.Protocol != setting.UnixSocket && setting.Protocol != setting.FCGIUnix {
|
||||
listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort)
|
||||
}
|
||||
_, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: Gitea Server", process.SystemProcessType, true)
|
||||
defer finished()
|
||||
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
|
||||
// This can be useful for users, many users do wrong to their config and get strange behaviors behind a reverse-proxy.
|
||||
// A user may fix the configuration mistake when he sees this log.
|
||||
// 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")
|
||||
@@ -230,36 +198,35 @@ func listen(m http.Handler, handleRedirector bool) error {
|
||||
if handleRedirector {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runHTTP("tcp", listenAddr, "Web", m)
|
||||
err = runHTTP("tcp", listenAddr, "Web", context2.ClearHandler(m))
|
||||
case setting.HTTPS:
|
||||
if setting.EnableAcme {
|
||||
err = runACME(listenAddr, m)
|
||||
if setting.EnableLetsEncrypt {
|
||||
err = runLetsEncrypt(listenAddr, setting.Domain, setting.LetsEncryptDirectory, setting.LetsEncryptEmail, context2.ClearHandler(m))
|
||||
break
|
||||
} else {
|
||||
if handleRedirector {
|
||||
if setting.RedirectOtherPort {
|
||||
go runHTTPRedirector()
|
||||
} else {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
}
|
||||
err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, m)
|
||||
}
|
||||
if handleRedirector {
|
||||
if setting.RedirectOtherPort {
|
||||
go runHTTPRedirector()
|
||||
} else {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
}
|
||||
err = runHTTPS("tcp", listenAddr, "Web", setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
|
||||
case setting.FCGI:
|
||||
if handleRedirector {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runFCGI("tcp", listenAddr, "FCGI Web", m)
|
||||
case setting.HTTPUnix:
|
||||
err = runFCGI("tcp", listenAddr, "FCGI Web", context2.ClearHandler(m))
|
||||
case setting.UnixSocket:
|
||||
if handleRedirector {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runHTTP("unix", listenAddr, "Web", m)
|
||||
err = runHTTP("unix", listenAddr, "Web", context2.ClearHandler(m))
|
||||
case setting.FCGIUnix:
|
||||
if handleRedirector {
|
||||
NoHTTPRedirector()
|
||||
}
|
||||
err = runFCGI("unix", listenAddr, "Web", m)
|
||||
err = runFCGI("unix", listenAddr, "Web", context2.ClearHandler(m))
|
||||
default:
|
||||
log.Fatal("Invalid protocol: %s", setting.Protocol)
|
||||
}
|
||||
|
136
cmd/web_acme.go
136
cmd/web_acme.go
@@ -1,136 +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 (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
)
|
||||
|
||||
func getCARoot(path string) (*x509.CertPool, error) {
|
||||
r, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block, _ := pem.Decode(r)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("no PEM found in the file %s", path)
|
||||
}
|
||||
caRoot, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certPool := x509.NewCertPool()
|
||||
certPool.AddCert(caRoot)
|
||||
return certPool, nil
|
||||
}
|
||||
|
||||
func runACME(listenAddr string, m http.Handler) error {
|
||||
// If HTTP Challenge enabled, needs to be serving on port 80. For TLSALPN needs 443.
|
||||
// Due to docker port mapping this can't be checked programmatically
|
||||
// TODO: these are placeholders until we add options for each in settings with appropriate warning
|
||||
enableHTTPChallenge := true
|
||||
enableTLSALPNChallenge := true
|
||||
altHTTPPort := 0
|
||||
altTLSALPNPort := 0
|
||||
|
||||
if p, err := strconv.Atoi(setting.PortToRedirect); err == nil {
|
||||
altHTTPPort = p
|
||||
}
|
||||
if p, err := strconv.Atoi(setting.HTTPPort); err == nil {
|
||||
altTLSALPNPort = p
|
||||
}
|
||||
|
||||
magic := certmagic.NewDefault()
|
||||
magic.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory}
|
||||
// Try to use private CA root if provided, otherwise defaults to system's trust
|
||||
var certPool *x509.CertPool
|
||||
if setting.AcmeCARoot != "" {
|
||||
var err error
|
||||
certPool, err = getCARoot(setting.AcmeCARoot)
|
||||
if err != nil {
|
||||
log.Warn("Failed to parse CA Root certificate, using default CA trust: %v", err)
|
||||
}
|
||||
}
|
||||
myACME := certmagic.NewACMEIssuer(magic, certmagic.ACMEIssuer{
|
||||
CA: setting.AcmeURL,
|
||||
TrustedRoots: certPool,
|
||||
Email: setting.AcmeEmail,
|
||||
Agreed: setting.AcmeTOS,
|
||||
DisableHTTPChallenge: !enableHTTPChallenge,
|
||||
DisableTLSALPNChallenge: !enableTLSALPNChallenge,
|
||||
ListenHost: setting.HTTPAddr,
|
||||
AltTLSALPNPort: altTLSALPNPort,
|
||||
AltHTTPPort: altHTTPPort,
|
||||
})
|
||||
|
||||
magic.Issuers = []certmagic.Issuer{myACME}
|
||||
|
||||
// this obtains certificates or renews them if necessary
|
||||
err := magic.ManageSync(graceful.GetManager().HammerContext(), []string{setting.Domain})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tlsConfig := magic.TLSConfig()
|
||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
|
||||
|
||||
if version := toTLSVersion(setting.SSLMinimumVersion); version != 0 {
|
||||
tlsConfig.MinVersion = version
|
||||
}
|
||||
if version := toTLSVersion(setting.SSLMaximumVersion); version != 0 {
|
||||
tlsConfig.MaxVersion = version
|
||||
}
|
||||
|
||||
// Set curve preferences
|
||||
if curves := toCurvePreferences(setting.SSLCurvePreferences); len(curves) > 0 {
|
||||
tlsConfig.CurvePreferences = curves
|
||||
}
|
||||
|
||||
// Set cipher suites
|
||||
if ciphers := toTLSCiphers(setting.SSLCipherSuites); len(ciphers) > 0 {
|
||||
tlsConfig.CipherSuites = ciphers
|
||||
}
|
||||
|
||||
if enableHTTPChallenge {
|
||||
go func() {
|
||||
_, _, finished := process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), "Web: ACME HTTP challenge server", process.SystemProcessType, true)
|
||||
defer finished()
|
||||
|
||||
log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
|
||||
// all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
|
||||
err := runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)))
|
||||
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.StatusTemporaryRedirect)
|
||||
}
|
@@ -5,20 +5,27 @@
|
||||
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 runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler) error {
|
||||
return graceful.HTTPListenAndServeTLS(network, listenAddr, name, certFile, keyFile, m)
|
||||
}
|
||||
|
||||
func runHTTPSWithTLSConfig(network, listenAddr, name string, tlsConfig *tls.Config, m http.Handler) error {
|
||||
return graceful.HTTPListenAndServeTLSConfig(network, listenAddr, name, tlsConfig, m)
|
||||
}
|
||||
|
||||
// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
|
||||
func NoHTTPRedirector() {
|
||||
graceful.GetManager().InformCleanup()
|
||||
@@ -41,12 +48,7 @@ func runFCGI(network, listenAddr, name string, m http.Handler) error {
|
||||
fcgiServer := graceful.NewServer(network, listenAddr, name)
|
||||
|
||||
err := fcgiServer.ListenAndServe(func(listener net.Listener) error {
|
||||
return fcgi.Serve(listener, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
if setting.AppSubURL != "" {
|
||||
req.URL.Path = strings.TrimPrefix(req.URL.Path, setting.AppSubURL)
|
||||
}
|
||||
m.ServeHTTP(resp, req)
|
||||
}))
|
||||
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)
|
||||
}
|
69
cmd/web_letsencrypt.go
Normal file
69
cmd/web_letsencrypt.go
Normal file
@@ -0,0 +1,69 @@
|
||||
// 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"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
context2 "github.com/gorilla/context"
|
||||
)
|
||||
|
||||
func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler) error {
|
||||
|
||||
// If HTTP Challenge enabled, needs to be serving on port 80. For TLSALPN needs 443.
|
||||
// Due to docker port mapping this can't be checked programatically
|
||||
// TODO: these are placeholders until we add options for each in settings with appropriate warning
|
||||
enableHTTPChallenge := true
|
||||
enableTLSALPNChallenge := true
|
||||
|
||||
magic := certmagic.NewDefault()
|
||||
magic.Storage = &certmagic.FileStorage{Path: directory}
|
||||
myACME := certmagic.NewACMEManager(magic, certmagic.ACMEManager{
|
||||
Email: email,
|
||||
Agreed: setting.LetsEncryptTOS,
|
||||
DisableHTTPChallenge: !enableHTTPChallenge,
|
||||
DisableTLSALPNChallenge: !enableTLSALPNChallenge,
|
||||
})
|
||||
|
||||
magic.Issuer = myACME
|
||||
|
||||
// this obtains certificates or renews them if necessary
|
||||
err := magic.ManageSync([]string{domain})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tlsConfig := magic.TLSConfig()
|
||||
|
||||
if enableHTTPChallenge {
|
||||
go func() {
|
||||
log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
|
||||
// all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
|
||||
var err = runHTTP("tcp", setting.HTTPAddr+":"+setting.PortToRedirect, "Let's Encrypt HTTP Challenge", myACME.HTTPChallengeHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)))
|
||||
if err != nil {
|
||||
log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return runHTTPSWithTLSConfig("tcp", listenAddr, "Web", tlsConfig, context2.ClearHandler(m))
|
||||
}
|
||||
|
||||
func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "GET" && r.Method != "HEAD" {
|
||||
http.Error(w, "Use HTTPS", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// Remove the trailing slash at the end of setting.AppURL, the request
|
||||
// URI always contains a leading slash, which would result in a double
|
||||
// slash
|
||||
target := strings.TrimSuffix(setting.AppURL, "/") + r.URL.RequestURI()
|
||||
http.Redirect(w, r, target, http.StatusFound)
|
||||
}
|
@@ -110,8 +110,6 @@ func runEnvironmentToIni(c *cli.Context) error {
|
||||
}
|
||||
cfg.NameMapper = ini.SnackCase
|
||||
|
||||
changed := false
|
||||
|
||||
prefix := c.String("prefix") + "__"
|
||||
|
||||
for _, kv := range os.Environ() {
|
||||
@@ -145,22 +143,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 +216,7 @@ func DecodeSectionKey(encoded string) (string, string) {
|
||||
if !inKey {
|
||||
if splitter := strings.Index(remaining, "__"); splitter > -1 {
|
||||
section += remaining[:splitter]
|
||||
inKey = true
|
||||
key += remaining[splitter+2:]
|
||||
} else {
|
||||
section += remaining
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
#############################################################################
|
||||
# This script sets some defaults for gitea to run in a FHS compliant manner #
|
||||
#############################################################################
|
||||
########################################################################
|
||||
# This script some defaults for gitea to run in a FHS compliant manner #
|
||||
########################################################################
|
||||
|
||||
# It assumes that you place this script as gitea in /usr/bin
|
||||
#
|
||||
@@ -33,8 +33,10 @@ for i in "$@"; do
|
||||
done
|
||||
|
||||
if [ -z "$APP_INI_SET" ]; then
|
||||
CONF_ARG=("-c" "${GITEA_APP_INI:-$APP_INI}")
|
||||
CONF_ARG="-c \"$APP_INI\""
|
||||
fi
|
||||
|
||||
# Provide FHS compliant defaults
|
||||
GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" exec -a "$0" "$GITEA" "${CONF_ARG[@]}" "$@"
|
||||
# Provide FHS compliant defaults to
|
||||
GITEA_WORK_DIR="${GITEA_WORK_DIR:-$WORK_DIR}" "$GITEA" $CONF_ARG "$@"
|
||||
|
||||
|
||||
|
@@ -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), 0o644); err != nil {
|
||||
if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil {
|
||||
return fmt.Errorf("%s: %+v", path, err)
|
||||
}
|
||||
fmt.Printf("%s created.\n", path)
|
||||
|
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')
|
@@ -7,10 +7,10 @@
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"buildFlags": "",
|
||||
"port": 2345,
|
||||
"host": "127.0.0.1",
|
||||
"program": "${workspaceRoot}/main.go",
|
||||
"env": {
|
||||
"GITEA_WORK_DIR": "${workspaceRoot}",
|
||||
},
|
||||
"env": {},
|
||||
"args": ["web"],
|
||||
"showLog": true
|
||||
},
|
||||
@@ -20,10 +20,10 @@
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"buildFlags": "-tags='sqlite sqlite_unlock_notify'",
|
||||
"port": 2345,
|
||||
"host": "127.0.0.1",
|
||||
"program": "${workspaceRoot}/main.go",
|
||||
"env": {
|
||||
"GITEA_WORK_DIR": "${workspaceRoot}",
|
||||
},
|
||||
"env": {},
|
||||
"args": ["web"],
|
||||
"showLog": true
|
||||
}
|
||||
|
@@ -1,35 +0,0 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
USE_PROCD=1
|
||||
|
||||
# PROCD_DEBUG=1
|
||||
|
||||
START=90
|
||||
STOP=10
|
||||
|
||||
PROG=/opt/gitea/gitea
|
||||
GITEA_WORK_DIR=/opt/gitea
|
||||
CONF_FILE=$GITEA_WORK_DIR/app.ini
|
||||
|
||||
start_service(){
|
||||
procd_open_instance gitea
|
||||
procd_set_param env GITEA_WORK_DIR=$GITEA_WORK_DIR
|
||||
procd_set_param env HOME=$GITEA_WORK_DIR
|
||||
procd_set_param command $PROG web -c $CONF_FILE
|
||||
procd_set_param file $CONF_FILE
|
||||
procd_set_param user git
|
||||
procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5} # respawn automatically if something died, be careful if you have an alternative process supervisor
|
||||
procd_close_instance
|
||||
}
|
||||
|
||||
start(){
|
||||
service_start $PROG
|
||||
}
|
||||
|
||||
stop(){
|
||||
service_stop $PROG
|
||||
}
|
||||
|
||||
reload(){
|
||||
service_reload $PROG
|
||||
}
|
@@ -12,6 +12,7 @@ import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -24,19 +25,18 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
gitea_git "code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/external"
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"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"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
@@ -49,13 +49,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 +80,43 @@ 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")
|
||||
//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
|
||||
// x.ShowSQL(true)
|
||||
err = unittest.InitFixtures(
|
||||
unittest.FixturesOptions{
|
||||
Dir: path.Join(curDir, "models/fixtures/"),
|
||||
},
|
||||
models.HasEngine = true
|
||||
//x.ShowSQL(true)
|
||||
err = models.InitFixtures(
|
||||
path.Join(curDir, "models/fixtures/"),
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Printf("Error initializing test database: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
unittest.LoadFixtures()
|
||||
models.LoadFixtures()
|
||||
util.RemoveAll(setting.RepoRootPath)
|
||||
util.RemoveAll(repo_module.LocalCopyPath())
|
||||
unittest.CopyDir(path.Join(curDir, "integrations/gitea-repositories-meta"), setting.RepoRootPath)
|
||||
util.RemoveAll(models.LocalCopyPath())
|
||||
util.CopyDir(path.Join(curDir, "integrations/gitea-repositories-meta"), setting.RepoRootPath)
|
||||
|
||||
log.Printf("[PR] Setting up router\n")
|
||||
// routers.GlobalInit()
|
||||
external.RegisterRenderers()
|
||||
//routers.GlobalInit()
|
||||
external.RegisterParsers()
|
||||
markup.Init()
|
||||
c := routers.NormalRoutes()
|
||||
c := routes.NormalRoutes()
|
||||
|
||||
log.Printf("[PR] Ready for testing !\n")
|
||||
log.Printf("[PR] Login with user1, user2, user3, ... with pass: password\n")
|
||||
@@ -137,8 +135,8 @@ func runPR() {
|
||||
}
|
||||
*/
|
||||
|
||||
// Start the server
|
||||
http.ListenAndServe(":8080", c)
|
||||
//Start the server
|
||||
http.ListenAndServe(":8080", context2.ClearHandler(c))
|
||||
|
||||
log.Printf("[PR] Cleaning up ...\n")
|
||||
/*
|
||||
@@ -160,7 +158,7 @@ func runPR() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
runPRFlag := flag.Bool("run", false, "Run the PR code")
|
||||
var runPRFlag = flag.Bool("run", false, "Run the PR code")
|
||||
flag.Parse()
|
||||
if *runPRFlag {
|
||||
runPR()
|
||||
@@ -173,16 +171,16 @@ func main() {
|
||||
force = false
|
||||
}
|
||||
|
||||
// Otherwise checkout PR
|
||||
//Otherwise checkout PR
|
||||
if len(os.Args) != 2 {
|
||||
log.Fatal("Need only one arg: the PR number")
|
||||
}
|
||||
pr := os.Args[1]
|
||||
|
||||
codeFilePath = filepath.FromSlash(codeFilePath) // Convert to running OS
|
||||
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)
|
||||
//Copy this file if it will not exist in the PR branch
|
||||
dat, err := ioutil.ReadFile(codeFilePath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to cache this code file : %v", err)
|
||||
}
|
||||
@@ -192,16 +190,16 @@ func main() {
|
||||
log.Fatalf("Failed to open the repo : %v", err)
|
||||
}
|
||||
|
||||
// Find remote upstream
|
||||
//Find remote upstream
|
||||
remotes, err := repo.Remotes()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to list remotes of repo : %v", err)
|
||||
}
|
||||
remoteUpstream := "origin" // Default
|
||||
remoteUpstream := "origin" //Default
|
||||
for _, r := range remotes {
|
||||
if r.Config().URLs[0] == "https://github.com/go-gitea/gitea.git" ||
|
||||
r.Config().URLs[0] == "https://github.com/go-gitea/gitea" ||
|
||||
r.Config().URLs[0] == "git@github.com:go-gitea/gitea.git" { // fetch at index 0
|
||||
r.Config().URLs[0] == "git@github.com:go-gitea/gitea.git" { //fetch at index 0
|
||||
remoteUpstream = r.Config().Name
|
||||
break
|
||||
}
|
||||
@@ -212,10 +210,10 @@ func main() {
|
||||
|
||||
log.Printf("Fetching PR #%s in %s\n", pr, branch)
|
||||
if runtime.GOOS == "windows" {
|
||||
// Use git cli command for windows
|
||||
//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{
|
||||
@@ -240,23 +238,22 @@ func main() {
|
||||
log.Fatalf("Failed to checkout %s : %v", branch, err)
|
||||
}
|
||||
|
||||
// Copy this file if not exist
|
||||
//Copy this file if not exist
|
||||
if _, err := os.Stat(codeFilePath); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(filepath.Dir(codeFilePath), 0o755)
|
||||
err = os.MkdirAll(filepath.Dir(codeFilePath), 0755)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to duplicate this code file in PR : %v", err)
|
||||
}
|
||||
err = os.WriteFile(codeFilePath, dat, 0o644)
|
||||
err = ioutil.WriteFile(codeFilePath, dat, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to duplicate this code file in PR : %v", err)
|
||||
}
|
||||
}
|
||||
// Force build of js, css, bin, ...
|
||||
//Force build of js, css, bin, ...
|
||||
runCmd("make", "build")
|
||||
// Start with integration test
|
||||
//Start with integration test
|
||||
runCmd("go", "run", "-mod", "vendor", "-tags", "sqlite sqlite_unlock_notify", codeFilePath, "-run")
|
||||
}
|
||||
|
||||
func runCmd(cmd ...string) {
|
||||
log.Printf("Executing : %s ...\n", cmd)
|
||||
c := exec.Command(cmd[0], cmd[1:]...)
|
||||
|
@@ -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
|
||||
|
@@ -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 -m "update $line"
|
||||
done
|
@@ -1,126 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# This is an update script for gitea installed via the binary distribution
|
||||
# from dl.gitea.io on linux as systemd service. It performs a backup and updates
|
||||
# Gitea in place.
|
||||
# NOTE: This adds the GPG Signing Key of the Gitea maintainers to the keyring.
|
||||
# Depends on: bash, curl, xz, sha256sum. optionally jq, gpg
|
||||
# See section below for available environment vars.
|
||||
# When no version is specified, updates to the latest release.
|
||||
# Examples:
|
||||
# upgrade.sh 1.15.10
|
||||
# giteahome=/opt/gitea giteaconf=$giteahome/app.ini upgrade.sh
|
||||
|
||||
# apply variables from environment
|
||||
: "${giteabin:="/usr/local/bin/gitea"}"
|
||||
: "${giteahome:="/var/lib/gitea"}"
|
||||
: "${giteaconf:="/etc/gitea/app.ini"}"
|
||||
: "${giteauser:="git"}"
|
||||
: "${sudocmd:="sudo"}"
|
||||
: "${arch:="linux-amd64"}"
|
||||
: "${service_start:="$sudocmd systemctl start gitea"}"
|
||||
: "${service_stop:="$sudocmd systemctl stop gitea"}"
|
||||
: "${service_status:="$sudocmd systemctl status gitea"}"
|
||||
: "${backupopts:=""}" # see `gitea dump --help` for available options
|
||||
|
||||
function giteacmd {
|
||||
if [[ $sudocmd = "su" ]]; then
|
||||
# `-c` only accept one string as argument.
|
||||
"$sudocmd" - "$giteauser" -c "$(printf "%q " "$giteabin" "--config" "$giteaconf" "--work-path" "$giteahome" "$@")"
|
||||
else
|
||||
"$sudocmd" --user "$giteauser" "$giteabin" --config "$giteaconf" --work-path "$giteahome" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
function require {
|
||||
for exe in "$@"; do
|
||||
command -v "$exe" &>/dev/null || (echo "missing dependency '$exe'"; exit 1)
|
||||
done
|
||||
}
|
||||
|
||||
# parse command line arguments
|
||||
while true; do
|
||||
case "$1" in
|
||||
-v | --version ) giteaversion="$2"; shift 2 ;;
|
||||
-y | --yes ) no_confirm="yes"; shift ;;
|
||||
--ignore-gpg) ignore_gpg="yes"; shift ;;
|
||||
"" | -- ) shift; break ;;
|
||||
* ) echo "Usage: [<environment vars>] upgrade.sh [-v <version>] [-y] [--ignore-gpg]"; exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
# exit once any command fails. this means that each step should be idempotent!
|
||||
set -euo pipefail
|
||||
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
os_release=$(cat /etc/os-release)
|
||||
|
||||
if [[ "$os_release" =~ "OpenWrt" ]]; then
|
||||
sudocmd="su"
|
||||
service_start="/etc/init.d/gitea start"
|
||||
service_stop="/etc/init.d/gitea stop"
|
||||
service_status="/etc/init.d/gitea status"
|
||||
else
|
||||
require systemctl
|
||||
fi
|
||||
fi
|
||||
|
||||
require curl xz sha256sum "$sudocmd"
|
||||
|
||||
# select version to install
|
||||
if [[ -z "${giteaversion:-}" ]]; then
|
||||
require jq
|
||||
giteaversion=$(curl --connect-timeout 10 -sL https://dl.gitea.io/gitea/version.json | jq -r .latest.version)
|
||||
echo "Latest available version is $giteaversion"
|
||||
fi
|
||||
|
||||
# confirm update
|
||||
echo "Checking currently installed version..."
|
||||
current=$(giteacmd --version | cut -d ' ' -f 3)
|
||||
[[ "$current" == "$giteaversion" ]] && echo "$current is already installed, stopping." && exit 1
|
||||
if [[ -z "${no_confirm:-}" ]]; then
|
||||
echo "Make sure to read the changelog first: https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md"
|
||||
echo "Are you ready to update Gitea from ${current} to ${giteaversion}? (y/N)"
|
||||
read -r confirm
|
||||
[[ "$confirm" == "y" ]] || [[ "$confirm" == "Y" ]] || exit 1
|
||||
fi
|
||||
|
||||
echo "Upgrading gitea from $current to $giteaversion ..."
|
||||
|
||||
pushd "$(pwd)" &>/dev/null
|
||||
cd "$giteahome" # needed for gitea dump later
|
||||
|
||||
# download new binary
|
||||
binname="gitea-${giteaversion}-${arch}"
|
||||
binurl="https://dl.gitea.io/gitea/${giteaversion}/${binname}.xz"
|
||||
echo "Downloading $binurl..."
|
||||
curl --connect-timeout 10 --silent --show-error --fail --location -O "$binurl{,.sha256,.asc}"
|
||||
|
||||
# validate checksum & gpg signature
|
||||
sha256sum -c "${binname}.xz.sha256"
|
||||
if [[ -z "${ignore_gpg:-}" ]]; then
|
||||
require gpg
|
||||
gpg --keyserver keys.openpgp.org --recv 7C9E68152594688862D62AF62D9AE806EC1592E2
|
||||
gpg --verify "${binname}.xz.asc" "${binname}.xz" || { echo 'Signature does not match'; exit 1; }
|
||||
fi
|
||||
rm "${binname}".xz.{sha256,asc}
|
||||
|
||||
# unpack binary + make executable
|
||||
xz --decompress --force "${binname}.xz"
|
||||
chown "$giteauser" "$binname"
|
||||
chmod +x "$binname"
|
||||
|
||||
# stop gitea, create backup, replace binary, restart gitea
|
||||
echo "Flushing gitea queues at $(date)"
|
||||
giteacmd manager flush-queues
|
||||
echo "Stopping gitea at $(date)"
|
||||
$service_stop
|
||||
echo "Creating backup in $giteahome"
|
||||
giteacmd dump $backupopts
|
||||
echo "Updating binary at $giteabin"
|
||||
cp -f "$giteabin" "$giteabin.bak" && mv -f "$binname" "$giteabin"
|
||||
$service_start
|
||||
$service_status
|
||||
|
||||
echo "Upgrade to $giteaversion successful!"
|
||||
|
||||
popd
|
File diff suppressed because it is too large
Load Diff
@@ -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}}-rootless
|
||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/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
|
||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/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
|
||||
image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64-rootless
|
||||
platform:
|
||||
architecture: arm64
|
||||
os: linux
|
||||
|
@@ -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
|
||||
|
@@ -2,5 +2,5 @@
|
||||
[[ -f ./setup ]] && source ./setup
|
||||
|
||||
pushd /app/gitea >/dev/null
|
||||
exec su-exec $USER /usr/local/bin/gitea web
|
||||
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"} \
|
||||
|
@@ -24,33 +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}" \
|
||||
SSH_INCLUDE_FILE="${SSH_INCLUDE_FILE:+"Include "}${SSH_INCLUDE_FILE}" \
|
||||
SSH_LOG_LEVEL=${SSH_LOG_LEVEL:-"INFO"} \
|
||||
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