mirror of https://github.com/go-gitea/gitea.git
Merge branch 'main' into lunny/uniform-temp-dir
This commit is contained in:
commit
887c8a5599
|
@ -22,20 +22,25 @@ groups:
|
|||
name: FEATURES
|
||||
labels:
|
||||
- type/feature
|
||||
-
|
||||
name: API
|
||||
labels:
|
||||
- modifies/api
|
||||
-
|
||||
name: ENHANCEMENTS
|
||||
labels:
|
||||
- type/enhancement
|
||||
- type/refactoring
|
||||
- topic/ui
|
||||
-
|
||||
name: PERFORMANCE
|
||||
labels:
|
||||
- performance/memory
|
||||
- performance/speed
|
||||
- performance/bigrepo
|
||||
- performance/cpu
|
||||
-
|
||||
name: BUGFIXES
|
||||
labels:
|
||||
- type/bug
|
||||
-
|
||||
name: API
|
||||
labels:
|
||||
- modifies/api
|
||||
-
|
||||
name: TESTING
|
||||
labels:
|
||||
|
|
|
@ -79,18 +79,6 @@ cpu.out
|
|||
/public/assets/fonts
|
||||
/public/assets/img/avatar
|
||||
/vendor
|
||||
/web_src/fomantic/node_modules
|
||||
/web_src/fomantic/build/*
|
||||
!/web_src/fomantic/build/semantic.js
|
||||
!/web_src/fomantic/build/semantic.css
|
||||
!/web_src/fomantic/build/themes
|
||||
/web_src/fomantic/build/themes/*
|
||||
!/web_src/fomantic/build/themes/default
|
||||
/web_src/fomantic/build/themes/default/assets/*
|
||||
!/web_src/fomantic/build/themes/default/assets/fonts
|
||||
/web_src/fomantic/build/themes/default/assets/fonts/*
|
||||
!/web_src/fomantic/build/themes/default/assets/fonts/icons.woff2
|
||||
!/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
|
||||
/VERSION
|
||||
/.air
|
||||
/.go-licenses
|
||||
|
|
|
@ -17,6 +17,7 @@ insert_final_newline = false
|
|||
|
||||
[templates/swagger/v1_json.tmpl]
|
||||
indent_style = space
|
||||
insert_final_newline = false
|
||||
|
||||
[templates/user/auth/oidc_wellknown.tmpl]
|
||||
indent_style = space
|
||||
|
|
File diff suppressed because it is too large
Load Diff
963
.eslintrc.yaml
963
.eslintrc.yaml
|
@ -1,963 +0,0 @@
|
|||
root: true
|
||||
reportUnusedDisableDirectives: true
|
||||
|
||||
ignorePatterns:
|
||||
- /web_src/js/vendor
|
||||
- /web_src/fomantic
|
||||
- /public/assets/js
|
||||
|
||||
parser: "@typescript-eslint/parser"
|
||||
|
||||
parserOptions:
|
||||
sourceType: module
|
||||
ecmaVersion: latest
|
||||
project: true
|
||||
extraFileExtensions: [".vue"]
|
||||
parser: "@typescript-eslint/parser" # for vue plugin - https://eslint.vuejs.org/user-guide/#how-to-use-a-custom-parser
|
||||
|
||||
settings:
|
||||
import/extensions: [".js", ".ts"]
|
||||
import/parsers:
|
||||
"@typescript-eslint/parser": [".js", ".ts"]
|
||||
import/resolver:
|
||||
typescript: true
|
||||
|
||||
plugins:
|
||||
- "@eslint-community/eslint-plugin-eslint-comments"
|
||||
- "@stylistic/eslint-plugin-js"
|
||||
- "@typescript-eslint/eslint-plugin"
|
||||
- eslint-plugin-array-func
|
||||
- eslint-plugin-deprecation
|
||||
- eslint-plugin-github
|
||||
- eslint-plugin-i
|
||||
- eslint-plugin-no-jquery
|
||||
- eslint-plugin-no-use-extend-native
|
||||
- eslint-plugin-regexp
|
||||
- eslint-plugin-sonarjs
|
||||
- eslint-plugin-unicorn
|
||||
- eslint-plugin-vitest
|
||||
- eslint-plugin-vitest-globals
|
||||
- eslint-plugin-wc
|
||||
|
||||
env:
|
||||
es2024: true
|
||||
node: true
|
||||
|
||||
overrides:
|
||||
- files: ["web_src/**/*"]
|
||||
globals:
|
||||
__webpack_public_path__: true
|
||||
process: false # https://github.com/webpack/webpack/issues/15833
|
||||
- files: ["web_src/**/*", "docs/**/*"]
|
||||
env:
|
||||
browser: true
|
||||
node: false
|
||||
- files: ["web_src/**/*worker.*"]
|
||||
env:
|
||||
worker: true
|
||||
rules:
|
||||
no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, status, statusbar, stop, toolbar, top]
|
||||
- files: ["*.config.*"]
|
||||
rules:
|
||||
i/no-unused-modules: [0]
|
||||
- files: ["**/*.d.ts"]
|
||||
rules:
|
||||
i/no-unused-modules: [0]
|
||||
"@typescript-eslint/consistent-type-definitions": [0]
|
||||
"@typescript-eslint/consistent-type-imports": [0]
|
||||
- files: ["web_src/js/types.ts"]
|
||||
rules:
|
||||
i/no-unused-modules: [0]
|
||||
- files: ["**/*.test.*", "web_src/js/test/setup.ts"]
|
||||
env:
|
||||
vitest-globals/env: true
|
||||
rules:
|
||||
vitest/consistent-test-filename: [0]
|
||||
vitest/consistent-test-it: [0]
|
||||
vitest/expect-expect: [0]
|
||||
vitest/max-expects: [0]
|
||||
vitest/max-nested-describe: [0]
|
||||
vitest/no-alias-methods: [0]
|
||||
vitest/no-commented-out-tests: [0]
|
||||
vitest/no-conditional-expect: [0]
|
||||
vitest/no-conditional-in-test: [0]
|
||||
vitest/no-conditional-tests: [0]
|
||||
vitest/no-disabled-tests: [0]
|
||||
vitest/no-done-callback: [0]
|
||||
vitest/no-duplicate-hooks: [0]
|
||||
vitest/no-focused-tests: [0]
|
||||
vitest/no-hooks: [0]
|
||||
vitest/no-identical-title: [2]
|
||||
vitest/no-interpolation-in-snapshots: [0]
|
||||
vitest/no-large-snapshots: [0]
|
||||
vitest/no-mocks-import: [0]
|
||||
vitest/no-restricted-matchers: [0]
|
||||
vitest/no-restricted-vi-methods: [0]
|
||||
vitest/no-standalone-expect: [0]
|
||||
vitest/no-test-prefixes: [0]
|
||||
vitest/no-test-return-statement: [0]
|
||||
vitest/prefer-called-with: [0]
|
||||
vitest/prefer-comparison-matcher: [0]
|
||||
vitest/prefer-each: [0]
|
||||
vitest/prefer-equality-matcher: [0]
|
||||
vitest/prefer-expect-resolves: [0]
|
||||
vitest/prefer-hooks-in-order: [0]
|
||||
vitest/prefer-hooks-on-top: [2]
|
||||
vitest/prefer-lowercase-title: [0]
|
||||
vitest/prefer-mock-promise-shorthand: [0]
|
||||
vitest/prefer-snapshot-hint: [0]
|
||||
vitest/prefer-spy-on: [0]
|
||||
vitest/prefer-strict-equal: [0]
|
||||
vitest/prefer-to-be: [0]
|
||||
vitest/prefer-to-be-falsy: [0]
|
||||
vitest/prefer-to-be-object: [0]
|
||||
vitest/prefer-to-be-truthy: [0]
|
||||
vitest/prefer-to-contain: [0]
|
||||
vitest/prefer-to-have-length: [0]
|
||||
vitest/prefer-todo: [0]
|
||||
vitest/require-hook: [0]
|
||||
vitest/require-to-throw-message: [0]
|
||||
vitest/require-top-level-describe: [0]
|
||||
vitest/valid-describe-callback: [2]
|
||||
vitest/valid-expect: [2]
|
||||
vitest/valid-title: [2]
|
||||
- files: ["web_src/js/modules/fetch.ts", "web_src/js/standalone/**/*"]
|
||||
rules:
|
||||
no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression]
|
||||
- files: ["**/*.vue"]
|
||||
plugins:
|
||||
- eslint-plugin-vue
|
||||
- eslint-plugin-vue-scoped-css
|
||||
extends:
|
||||
- plugin:vue/vue3-recommended
|
||||
- plugin:vue-scoped-css/vue3-recommended
|
||||
rules:
|
||||
vue/attributes-order: [0]
|
||||
vue/html-closing-bracket-spacing: [2, {startTag: never, endTag: never, selfClosingTag: never}]
|
||||
vue/max-attributes-per-line: [0]
|
||||
vue/singleline-html-element-content-newline: [0]
|
||||
- files: ["tests/e2e/**"]
|
||||
plugins:
|
||||
- eslint-plugin-playwright
|
||||
extends: plugin:playwright/recommended
|
||||
|
||||
rules:
|
||||
"@eslint-community/eslint-comments/disable-enable-pair": [2]
|
||||
"@eslint-community/eslint-comments/no-aggregating-enable": [2]
|
||||
"@eslint-community/eslint-comments/no-duplicate-disable": [2]
|
||||
"@eslint-community/eslint-comments/no-restricted-disable": [0]
|
||||
"@eslint-community/eslint-comments/no-unlimited-disable": [2]
|
||||
"@eslint-community/eslint-comments/no-unused-disable": [2]
|
||||
"@eslint-community/eslint-comments/no-unused-enable": [2]
|
||||
"@eslint-community/eslint-comments/no-use": [0]
|
||||
"@eslint-community/eslint-comments/require-description": [0]
|
||||
"@stylistic/js/array-bracket-newline": [0]
|
||||
"@stylistic/js/array-bracket-spacing": [2, never]
|
||||
"@stylistic/js/array-element-newline": [0]
|
||||
"@stylistic/js/arrow-parens": [2, always]
|
||||
"@stylistic/js/arrow-spacing": [2, {before: true, after: true}]
|
||||
"@stylistic/js/block-spacing": [0]
|
||||
"@stylistic/js/brace-style": [2, 1tbs, {allowSingleLine: true}]
|
||||
"@stylistic/js/comma-dangle": [2, always-multiline]
|
||||
"@stylistic/js/comma-spacing": [2, {before: false, after: true}]
|
||||
"@stylistic/js/comma-style": [2, last]
|
||||
"@stylistic/js/computed-property-spacing": [2, never]
|
||||
"@stylistic/js/dot-location": [2, property]
|
||||
"@stylistic/js/eol-last": [2]
|
||||
"@stylistic/js/function-call-argument-newline": [0]
|
||||
"@stylistic/js/function-call-spacing": [2, never]
|
||||
"@stylistic/js/function-paren-newline": [0]
|
||||
"@stylistic/js/generator-star-spacing": [0]
|
||||
"@stylistic/js/implicit-arrow-linebreak": [0]
|
||||
"@stylistic/js/indent": [2, 2, {ignoreComments: true, SwitchCase: 1}]
|
||||
"@stylistic/js/key-spacing": [2]
|
||||
"@stylistic/js/keyword-spacing": [2]
|
||||
"@stylistic/js/line-comment-position": [0]
|
||||
"@stylistic/js/linebreak-style": [2, unix]
|
||||
"@stylistic/js/lines-around-comment": [0]
|
||||
"@stylistic/js/lines-between-class-members": [0]
|
||||
"@stylistic/js/max-len": [0]
|
||||
"@stylistic/js/max-statements-per-line": [0]
|
||||
"@stylistic/js/multiline-comment-style": [0]
|
||||
"@stylistic/js/multiline-ternary": [0]
|
||||
"@stylistic/js/new-parens": [2]
|
||||
"@stylistic/js/newline-per-chained-call": [0]
|
||||
"@stylistic/js/no-confusing-arrow": [0]
|
||||
"@stylistic/js/no-extra-parens": [0]
|
||||
"@stylistic/js/no-extra-semi": [2]
|
||||
"@stylistic/js/no-floating-decimal": [0]
|
||||
"@stylistic/js/no-mixed-operators": [0]
|
||||
"@stylistic/js/no-mixed-spaces-and-tabs": [2]
|
||||
"@stylistic/js/no-multi-spaces": [2, {ignoreEOLComments: true, exceptions: {Property: true}}]
|
||||
"@stylistic/js/no-multiple-empty-lines": [2, {max: 1, maxEOF: 0, maxBOF: 0}]
|
||||
"@stylistic/js/no-tabs": [2]
|
||||
"@stylistic/js/no-trailing-spaces": [2]
|
||||
"@stylistic/js/no-whitespace-before-property": [2]
|
||||
"@stylistic/js/nonblock-statement-body-position": [2]
|
||||
"@stylistic/js/object-curly-newline": [0]
|
||||
"@stylistic/js/object-curly-spacing": [2, never]
|
||||
"@stylistic/js/object-property-newline": [0]
|
||||
"@stylistic/js/one-var-declaration-per-line": [0]
|
||||
"@stylistic/js/operator-linebreak": [2, after]
|
||||
"@stylistic/js/padded-blocks": [2, never]
|
||||
"@stylistic/js/padding-line-between-statements": [0]
|
||||
"@stylistic/js/quote-props": [0]
|
||||
"@stylistic/js/quotes": [2, single, {avoidEscape: true, allowTemplateLiterals: true}]
|
||||
"@stylistic/js/rest-spread-spacing": [2, never]
|
||||
"@stylistic/js/semi": [2, always, {omitLastInOneLineBlock: true}]
|
||||
"@stylistic/js/semi-spacing": [2, {before: false, after: true}]
|
||||
"@stylistic/js/semi-style": [2, last]
|
||||
"@stylistic/js/space-before-blocks": [2, always]
|
||||
"@stylistic/js/space-before-function-paren": [2, {anonymous: ignore, named: never, asyncArrow: always}]
|
||||
"@stylistic/js/space-in-parens": [2, never]
|
||||
"@stylistic/js/space-infix-ops": [2]
|
||||
"@stylistic/js/space-unary-ops": [2]
|
||||
"@stylistic/js/spaced-comment": [2, always]
|
||||
"@stylistic/js/switch-colon-spacing": [2]
|
||||
"@stylistic/js/template-curly-spacing": [2, never]
|
||||
"@stylistic/js/template-tag-spacing": [2, never]
|
||||
"@stylistic/js/wrap-iife": [2, inside]
|
||||
"@stylistic/js/wrap-regex": [0]
|
||||
"@stylistic/js/yield-star-spacing": [2, after]
|
||||
"@typescript-eslint/adjacent-overload-signatures": [0]
|
||||
"@typescript-eslint/array-type": [0]
|
||||
"@typescript-eslint/await-thenable": [2]
|
||||
"@typescript-eslint/ban-ts-comment": [2, {'ts-expect-error': false, 'ts-ignore': true, 'ts-nocheck': false, 'ts-check': false}]
|
||||
"@typescript-eslint/ban-tslint-comment": [0]
|
||||
"@typescript-eslint/class-literal-property-style": [0]
|
||||
"@typescript-eslint/class-methods-use-this": [0]
|
||||
"@typescript-eslint/consistent-generic-constructors": [0]
|
||||
"@typescript-eslint/consistent-indexed-object-style": [0]
|
||||
"@typescript-eslint/consistent-return": [0]
|
||||
"@typescript-eslint/consistent-type-assertions": [2, {assertionStyle: as, objectLiteralTypeAssertions: allow}]
|
||||
"@typescript-eslint/consistent-type-definitions": [2, type]
|
||||
"@typescript-eslint/consistent-type-exports": [2, {fixMixedExportsWithInlineTypeSpecifier: false}]
|
||||
"@typescript-eslint/consistent-type-imports": [2, {prefer: type-imports, fixStyle: separate-type-imports, disallowTypeAnnotations: true}]
|
||||
"@typescript-eslint/default-param-last": [0]
|
||||
"@typescript-eslint/dot-notation": [0]
|
||||
"@typescript-eslint/explicit-function-return-type": [0]
|
||||
"@typescript-eslint/explicit-member-accessibility": [0]
|
||||
"@typescript-eslint/explicit-module-boundary-types": [0]
|
||||
"@typescript-eslint/init-declarations": [0]
|
||||
"@typescript-eslint/max-params": [0]
|
||||
"@typescript-eslint/member-ordering": [0]
|
||||
"@typescript-eslint/method-signature-style": [0]
|
||||
"@typescript-eslint/naming-convention": [0]
|
||||
"@typescript-eslint/no-array-constructor": [2]
|
||||
"@typescript-eslint/no-array-delete": [2]
|
||||
"@typescript-eslint/no-base-to-string": [0]
|
||||
"@typescript-eslint/no-confusing-non-null-assertion": [2]
|
||||
"@typescript-eslint/no-confusing-void-expression": [0]
|
||||
"@typescript-eslint/no-dupe-class-members": [0]
|
||||
"@typescript-eslint/no-duplicate-enum-values": [2]
|
||||
"@typescript-eslint/no-duplicate-type-constituents": [2, {ignoreUnions: true}]
|
||||
"@typescript-eslint/no-dynamic-delete": [0]
|
||||
"@typescript-eslint/no-empty-function": [0]
|
||||
"@typescript-eslint/no-empty-interface": [0]
|
||||
"@typescript-eslint/no-empty-object-type": [2]
|
||||
"@typescript-eslint/no-explicit-any": [0]
|
||||
"@typescript-eslint/no-extra-non-null-assertion": [2]
|
||||
"@typescript-eslint/no-extraneous-class": [0]
|
||||
"@typescript-eslint/no-floating-promises": [0]
|
||||
"@typescript-eslint/no-for-in-array": [2]
|
||||
"@typescript-eslint/no-implied-eval": [2]
|
||||
"@typescript-eslint/no-import-type-side-effects": [0] # dupe with consistent-type-imports
|
||||
"@typescript-eslint/no-inferrable-types": [0]
|
||||
"@typescript-eslint/no-invalid-this": [0]
|
||||
"@typescript-eslint/no-invalid-void-type": [0]
|
||||
"@typescript-eslint/no-loop-func": [0]
|
||||
"@typescript-eslint/no-loss-of-precision": [0]
|
||||
"@typescript-eslint/no-magic-numbers": [0]
|
||||
"@typescript-eslint/no-meaningless-void-operator": [0]
|
||||
"@typescript-eslint/no-misused-new": [2]
|
||||
"@typescript-eslint/no-misused-promises": [2, {checksVoidReturn: {attributes: false, arguments: false}}]
|
||||
"@typescript-eslint/no-mixed-enums": [0]
|
||||
"@typescript-eslint/no-namespace": [2]
|
||||
"@typescript-eslint/no-non-null-asserted-nullish-coalescing": [0]
|
||||
"@typescript-eslint/no-non-null-asserted-optional-chain": [2]
|
||||
"@typescript-eslint/no-non-null-assertion": [0]
|
||||
"@typescript-eslint/no-redeclare": [0]
|
||||
"@typescript-eslint/no-redundant-type-constituents": [2]
|
||||
"@typescript-eslint/no-require-imports": [2]
|
||||
"@typescript-eslint/no-restricted-imports": [0]
|
||||
"@typescript-eslint/no-restricted-types": [0]
|
||||
"@typescript-eslint/no-shadow": [0]
|
||||
"@typescript-eslint/no-this-alias": [0] # handled by unicorn/no-this-assignment
|
||||
"@typescript-eslint/no-unnecessary-boolean-literal-compare": [0]
|
||||
"@typescript-eslint/no-unnecessary-condition": [0]
|
||||
"@typescript-eslint/no-unnecessary-qualifier": [0]
|
||||
"@typescript-eslint/no-unnecessary-template-expression": [0]
|
||||
"@typescript-eslint/no-unnecessary-type-arguments": [0]
|
||||
"@typescript-eslint/no-unnecessary-type-assertion": [2]
|
||||
"@typescript-eslint/no-unnecessary-type-constraint": [2]
|
||||
"@typescript-eslint/no-unsafe-argument": [0]
|
||||
"@typescript-eslint/no-unsafe-assignment": [0]
|
||||
"@typescript-eslint/no-unsafe-call": [0]
|
||||
"@typescript-eslint/no-unsafe-declaration-merging": [2]
|
||||
"@typescript-eslint/no-unsafe-enum-comparison": [2]
|
||||
"@typescript-eslint/no-unsafe-function-type": [2]
|
||||
"@typescript-eslint/no-unsafe-member-access": [0]
|
||||
"@typescript-eslint/no-unsafe-return": [0]
|
||||
"@typescript-eslint/no-unsafe-unary-minus": [2]
|
||||
"@typescript-eslint/no-unused-expressions": [0]
|
||||
"@typescript-eslint/no-unused-vars": [2, {vars: all, args: all, caughtErrors: all, ignoreRestSiblings: false, argsIgnorePattern: ^_, varsIgnorePattern: ^_, caughtErrorsIgnorePattern: ^_, destructuredArrayIgnorePattern: ^_}]
|
||||
"@typescript-eslint/no-use-before-define": [0]
|
||||
"@typescript-eslint/no-useless-constructor": [0]
|
||||
"@typescript-eslint/no-useless-empty-export": [0]
|
||||
"@typescript-eslint/no-wrapper-object-types": [2]
|
||||
"@typescript-eslint/non-nullable-type-assertion-style": [0]
|
||||
"@typescript-eslint/only-throw-error": [2]
|
||||
"@typescript-eslint/parameter-properties": [0]
|
||||
"@typescript-eslint/prefer-as-const": [2]
|
||||
"@typescript-eslint/prefer-destructuring": [0]
|
||||
"@typescript-eslint/prefer-enum-initializers": [0]
|
||||
"@typescript-eslint/prefer-find": [2]
|
||||
"@typescript-eslint/prefer-for-of": [2]
|
||||
"@typescript-eslint/prefer-function-type": [2]
|
||||
"@typescript-eslint/prefer-includes": [2]
|
||||
"@typescript-eslint/prefer-literal-enum-member": [0]
|
||||
"@typescript-eslint/prefer-namespace-keyword": [0]
|
||||
"@typescript-eslint/prefer-nullish-coalescing": [0]
|
||||
"@typescript-eslint/prefer-optional-chain": [2, {requireNullish: true}]
|
||||
"@typescript-eslint/prefer-promise-reject-errors": [0]
|
||||
"@typescript-eslint/prefer-readonly": [0]
|
||||
"@typescript-eslint/prefer-readonly-parameter-types": [0]
|
||||
"@typescript-eslint/prefer-reduce-type-parameter": [0]
|
||||
"@typescript-eslint/prefer-regexp-exec": [0]
|
||||
"@typescript-eslint/prefer-return-this-type": [0]
|
||||
"@typescript-eslint/prefer-string-starts-ends-with": [2, {allowSingleElementEquality: always}]
|
||||
"@typescript-eslint/promise-function-async": [0]
|
||||
"@typescript-eslint/require-array-sort-compare": [0]
|
||||
"@typescript-eslint/require-await": [0]
|
||||
"@typescript-eslint/restrict-plus-operands": [2]
|
||||
"@typescript-eslint/restrict-template-expressions": [0]
|
||||
"@typescript-eslint/return-await": [0]
|
||||
"@typescript-eslint/strict-boolean-expressions": [0]
|
||||
"@typescript-eslint/switch-exhaustiveness-check": [0]
|
||||
"@typescript-eslint/triple-slash-reference": [2]
|
||||
"@typescript-eslint/typedef": [0]
|
||||
"@typescript-eslint/unbound-method": [0] # too many false-positives
|
||||
"@typescript-eslint/unified-signatures": [2]
|
||||
accessor-pairs: [2]
|
||||
array-callback-return: [2, {checkForEach: true}]
|
||||
array-func/avoid-reverse: [2]
|
||||
array-func/from-map: [2]
|
||||
array-func/no-unnecessary-this-arg: [2]
|
||||
array-func/prefer-array-from: [2]
|
||||
array-func/prefer-flat-map: [0] # handled by unicorn/prefer-array-flat-map
|
||||
array-func/prefer-flat: [0] # handled by unicorn/prefer-array-flat
|
||||
arrow-body-style: [0]
|
||||
block-scoped-var: [2]
|
||||
camelcase: [0]
|
||||
capitalized-comments: [0]
|
||||
class-methods-use-this: [0]
|
||||
complexity: [0]
|
||||
consistent-return: [0]
|
||||
consistent-this: [0]
|
||||
constructor-super: [2]
|
||||
curly: [0]
|
||||
default-case-last: [2]
|
||||
default-case: [0]
|
||||
default-param-last: [0]
|
||||
deprecation/deprecation: [2]
|
||||
dot-notation: [0]
|
||||
eqeqeq: [2]
|
||||
for-direction: [2]
|
||||
func-name-matching: [2]
|
||||
func-names: [0]
|
||||
func-style: [0]
|
||||
getter-return: [2]
|
||||
github/a11y-aria-label-is-well-formatted: [0]
|
||||
github/a11y-no-title-attribute: [0]
|
||||
github/a11y-no-visually-hidden-interactive-element: [0]
|
||||
github/a11y-role-supports-aria-props: [0]
|
||||
github/a11y-svg-has-accessible-name: [0]
|
||||
github/array-foreach: [0]
|
||||
github/async-currenttarget: [2]
|
||||
github/async-preventdefault: [2]
|
||||
github/authenticity-token: [0]
|
||||
github/get-attribute: [0]
|
||||
github/js-class-name: [0]
|
||||
github/no-blur: [0]
|
||||
github/no-d-none: [0]
|
||||
github/no-dataset: [2]
|
||||
github/no-dynamic-script-tag: [2]
|
||||
github/no-implicit-buggy-globals: [2]
|
||||
github/no-inner-html: [0]
|
||||
github/no-innerText: [2]
|
||||
github/no-then: [2]
|
||||
github/no-useless-passive: [2]
|
||||
github/prefer-observers: [2]
|
||||
github/require-passive-events: [2]
|
||||
github/unescaped-html-literal: [0]
|
||||
grouped-accessor-pairs: [2]
|
||||
guard-for-in: [0]
|
||||
id-blacklist: [0]
|
||||
id-length: [0]
|
||||
id-match: [0]
|
||||
i/consistent-type-specifier-style: [0]
|
||||
i/default: [0]
|
||||
i/dynamic-import-chunkname: [0]
|
||||
i/export: [2]
|
||||
i/exports-last: [0]
|
||||
i/extensions: [2, always, {ignorePackages: true}]
|
||||
i/first: [2]
|
||||
i/group-exports: [0]
|
||||
i/max-dependencies: [0]
|
||||
i/named: [2]
|
||||
i/namespace: [0]
|
||||
i/newline-after-import: [0]
|
||||
i/no-absolute-path: [0]
|
||||
i/no-amd: [2]
|
||||
i/no-anonymous-default-export: [0]
|
||||
i/no-commonjs: [2]
|
||||
i/no-cycle: [2, {ignoreExternal: true, maxDepth: 1}]
|
||||
i/no-default-export: [0]
|
||||
i/no-deprecated: [0]
|
||||
i/no-dynamic-require: [0]
|
||||
i/no-empty-named-blocks: [2]
|
||||
i/no-extraneous-dependencies: [2]
|
||||
i/no-import-module-exports: [0]
|
||||
i/no-internal-modules: [0]
|
||||
i/no-mutable-exports: [0]
|
||||
i/no-named-as-default-member: [0]
|
||||
i/no-named-as-default: [0]
|
||||
i/no-named-default: [0]
|
||||
i/no-named-export: [0]
|
||||
i/no-namespace: [0]
|
||||
i/no-nodejs-modules: [0]
|
||||
i/no-relative-packages: [0]
|
||||
i/no-relative-parent-imports: [0]
|
||||
i/no-restricted-paths: [0]
|
||||
i/no-self-import: [2]
|
||||
i/no-unassigned-import: [0]
|
||||
i/no-unresolved: [2, {commonjs: true, ignore: ["\\?.+$"]}]
|
||||
i/no-unused-modules: [2, {unusedExports: true}]
|
||||
i/no-useless-path-segments: [2, {commonjs: true}]
|
||||
i/no-webpack-loader-syntax: [2]
|
||||
i/order: [0]
|
||||
i/prefer-default-export: [0]
|
||||
i/unambiguous: [0]
|
||||
init-declarations: [0]
|
||||
line-comment-position: [0]
|
||||
logical-assignment-operators: [0]
|
||||
max-classes-per-file: [0]
|
||||
max-depth: [0]
|
||||
max-lines-per-function: [0]
|
||||
max-lines: [0]
|
||||
max-nested-callbacks: [0]
|
||||
max-params: [0]
|
||||
max-statements: [0]
|
||||
multiline-comment-style: [2, separate-lines]
|
||||
new-cap: [0]
|
||||
no-alert: [0]
|
||||
no-array-constructor: [0] # handled by @typescript-eslint/no-array-constructor
|
||||
no-async-promise-executor: [0]
|
||||
no-await-in-loop: [0]
|
||||
no-bitwise: [0]
|
||||
no-buffer-constructor: [0]
|
||||
no-caller: [2]
|
||||
no-case-declarations: [2]
|
||||
no-class-assign: [2]
|
||||
no-compare-neg-zero: [2]
|
||||
no-cond-assign: [2, except-parens]
|
||||
no-console: [1, {allow: [debug, info, warn, error]}]
|
||||
no-const-assign: [2]
|
||||
no-constant-binary-expression: [2]
|
||||
no-constant-condition: [0]
|
||||
no-constructor-return: [2]
|
||||
no-continue: [0]
|
||||
no-control-regex: [0]
|
||||
no-debugger: [1]
|
||||
no-delete-var: [2]
|
||||
no-div-regex: [0]
|
||||
no-dupe-args: [2]
|
||||
no-dupe-class-members: [2]
|
||||
no-dupe-else-if: [2]
|
||||
no-dupe-keys: [2]
|
||||
no-duplicate-case: [2]
|
||||
no-duplicate-imports: [0]
|
||||
no-else-return: [2]
|
||||
no-empty-character-class: [2]
|
||||
no-empty-function: [0]
|
||||
no-empty-pattern: [2]
|
||||
no-empty-static-block: [2]
|
||||
no-empty: [2, {allowEmptyCatch: true}]
|
||||
no-eq-null: [2]
|
||||
no-eval: [2]
|
||||
no-ex-assign: [2]
|
||||
no-extend-native: [2]
|
||||
no-extra-bind: [2]
|
||||
no-extra-boolean-cast: [2]
|
||||
no-extra-label: [0]
|
||||
no-fallthrough: [2]
|
||||
no-func-assign: [2]
|
||||
no-global-assign: [2]
|
||||
no-implicit-coercion: [2]
|
||||
no-implicit-globals: [0]
|
||||
no-implied-eval: [0] # handled by @typescript-eslint/no-implied-eval
|
||||
no-import-assign: [2]
|
||||
no-inline-comments: [0]
|
||||
no-inner-declarations: [2]
|
||||
no-invalid-regexp: [2]
|
||||
no-invalid-this: [0]
|
||||
no-irregular-whitespace: [2]
|
||||
no-iterator: [2]
|
||||
no-jquery/no-ajax-events: [2]
|
||||
no-jquery/no-ajax: [2]
|
||||
no-jquery/no-and-self: [2]
|
||||
no-jquery/no-animate-toggle: [2]
|
||||
no-jquery/no-animate: [2]
|
||||
no-jquery/no-append-html: [2]
|
||||
no-jquery/no-attr: [2]
|
||||
no-jquery/no-bind: [2]
|
||||
no-jquery/no-box-model: [2]
|
||||
no-jquery/no-browser: [2]
|
||||
no-jquery/no-camel-case: [2]
|
||||
no-jquery/no-class-state: [2]
|
||||
no-jquery/no-class: [0]
|
||||
no-jquery/no-clone: [2]
|
||||
no-jquery/no-closest: [0]
|
||||
no-jquery/no-constructor-attributes: [2]
|
||||
no-jquery/no-contains: [2]
|
||||
no-jquery/no-context-prop: [2]
|
||||
no-jquery/no-css: [2]
|
||||
no-jquery/no-data: [0]
|
||||
no-jquery/no-deferred: [2]
|
||||
no-jquery/no-delegate: [2]
|
||||
no-jquery/no-each-collection: [0]
|
||||
no-jquery/no-each-util: [0]
|
||||
no-jquery/no-each: [0]
|
||||
no-jquery/no-error-shorthand: [2]
|
||||
no-jquery/no-error: [2]
|
||||
no-jquery/no-escape-selector: [2]
|
||||
no-jquery/no-event-shorthand: [2]
|
||||
no-jquery/no-extend: [2]
|
||||
no-jquery/no-fade: [2]
|
||||
no-jquery/no-filter: [0]
|
||||
no-jquery/no-find-collection: [0]
|
||||
no-jquery/no-find-util: [2]
|
||||
no-jquery/no-find: [0]
|
||||
no-jquery/no-fx-interval: [2]
|
||||
no-jquery/no-global-eval: [2]
|
||||
no-jquery/no-global-selector: [0]
|
||||
no-jquery/no-grep: [2]
|
||||
no-jquery/no-has: [2]
|
||||
no-jquery/no-hold-ready: [2]
|
||||
no-jquery/no-html: [0]
|
||||
no-jquery/no-in-array: [2]
|
||||
no-jquery/no-is-array: [2]
|
||||
no-jquery/no-is-empty-object: [2]
|
||||
no-jquery/no-is-function: [2]
|
||||
no-jquery/no-is-numeric: [2]
|
||||
no-jquery/no-is-plain-object: [2]
|
||||
no-jquery/no-is-window: [2]
|
||||
no-jquery/no-is: [2]
|
||||
no-jquery/no-jquery-constructor: [0]
|
||||
no-jquery/no-live: [2]
|
||||
no-jquery/no-load-shorthand: [2]
|
||||
no-jquery/no-load: [2]
|
||||
no-jquery/no-map-collection: [0]
|
||||
no-jquery/no-map-util: [2]
|
||||
no-jquery/no-map: [2]
|
||||
no-jquery/no-merge: [2]
|
||||
no-jquery/no-node-name: [2]
|
||||
no-jquery/no-noop: [2]
|
||||
no-jquery/no-now: [2]
|
||||
no-jquery/no-on-ready: [2]
|
||||
no-jquery/no-other-methods: [0]
|
||||
no-jquery/no-other-utils: [2]
|
||||
no-jquery/no-param: [2]
|
||||
no-jquery/no-parent: [0]
|
||||
no-jquery/no-parents: [2]
|
||||
no-jquery/no-parse-html-literal: [2]
|
||||
no-jquery/no-parse-html: [2]
|
||||
no-jquery/no-parse-json: [2]
|
||||
no-jquery/no-parse-xml: [2]
|
||||
no-jquery/no-prop: [2]
|
||||
no-jquery/no-proxy: [2]
|
||||
no-jquery/no-ready-shorthand: [2]
|
||||
no-jquery/no-ready: [2]
|
||||
no-jquery/no-selector-prop: [2]
|
||||
no-jquery/no-serialize: [2]
|
||||
no-jquery/no-size: [2]
|
||||
no-jquery/no-sizzle: [2]
|
||||
no-jquery/no-slide: [2]
|
||||
no-jquery/no-sub: [2]
|
||||
no-jquery/no-support: [2]
|
||||
no-jquery/no-text: [2]
|
||||
no-jquery/no-trigger: [0]
|
||||
no-jquery/no-trim: [2]
|
||||
no-jquery/no-type: [2]
|
||||
no-jquery/no-unique: [2]
|
||||
no-jquery/no-unload-shorthand: [2]
|
||||
no-jquery/no-val: [0]
|
||||
no-jquery/no-visibility: [2]
|
||||
no-jquery/no-when: [2]
|
||||
no-jquery/no-wrap: [2]
|
||||
no-jquery/variable-pattern: [2]
|
||||
no-label-var: [2]
|
||||
no-labels: [0] # handled by no-restricted-syntax
|
||||
no-lone-blocks: [2]
|
||||
no-lonely-if: [0]
|
||||
no-loop-func: [0]
|
||||
no-loss-of-precision: [2]
|
||||
no-magic-numbers: [0]
|
||||
no-misleading-character-class: [2]
|
||||
no-multi-assign: [0]
|
||||
no-multi-str: [2]
|
||||
no-negated-condition: [0]
|
||||
no-nested-ternary: [0]
|
||||
no-new-func: [2]
|
||||
no-new-native-nonconstructor: [2]
|
||||
no-new-object: [2]
|
||||
no-new-symbol: [2]
|
||||
no-new-wrappers: [2]
|
||||
no-new: [0]
|
||||
no-nonoctal-decimal-escape: [2]
|
||||
no-obj-calls: [2]
|
||||
no-octal-escape: [2]
|
||||
no-octal: [2]
|
||||
no-param-reassign: [0]
|
||||
no-plusplus: [0]
|
||||
no-promise-executor-return: [0]
|
||||
no-proto: [2]
|
||||
no-prototype-builtins: [2]
|
||||
no-redeclare: [0] # must be disabled for typescript overloads
|
||||
no-regex-spaces: [2]
|
||||
no-restricted-exports: [0]
|
||||
no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, location, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, self, status, statusbar, stop, toolbar, top, __dirname, __filename]
|
||||
no-restricted-imports: [0]
|
||||
no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression, {selector: "CallExpression[callee.name='fetch']", message: "use modules/fetch.ts instead"}]
|
||||
no-return-assign: [0]
|
||||
no-script-url: [2]
|
||||
no-self-assign: [2, {props: true}]
|
||||
no-self-compare: [2]
|
||||
no-sequences: [2]
|
||||
no-setter-return: [2]
|
||||
no-shadow-restricted-names: [2]
|
||||
no-shadow: [0]
|
||||
no-sparse-arrays: [2]
|
||||
no-template-curly-in-string: [2]
|
||||
no-ternary: [0]
|
||||
no-this-before-super: [2]
|
||||
no-throw-literal: [2]
|
||||
no-undef-init: [2]
|
||||
no-undef: [2, {typeof: true}]
|
||||
no-undefined: [0]
|
||||
no-underscore-dangle: [0]
|
||||
no-unexpected-multiline: [2]
|
||||
no-unmodified-loop-condition: [2]
|
||||
no-unneeded-ternary: [2]
|
||||
no-unreachable-loop: [2]
|
||||
no-unreachable: [2]
|
||||
no-unsafe-finally: [2]
|
||||
no-unsafe-negation: [2]
|
||||
no-unused-expressions: [2]
|
||||
no-unused-labels: [2]
|
||||
no-unused-private-class-members: [2]
|
||||
no-unused-vars: [0] # handled by @typescript-eslint/no-unused-vars
|
||||
no-use-before-define: [2, {functions: false, classes: true, variables: true, allowNamedExports: true}]
|
||||
no-use-extend-native/no-use-extend-native: [2]
|
||||
no-useless-backreference: [2]
|
||||
no-useless-call: [2]
|
||||
no-useless-catch: [2]
|
||||
no-useless-computed-key: [2]
|
||||
no-useless-concat: [2]
|
||||
no-useless-constructor: [2]
|
||||
no-useless-escape: [2]
|
||||
no-useless-rename: [2]
|
||||
no-useless-return: [2]
|
||||
no-var: [2]
|
||||
no-void: [2]
|
||||
no-warning-comments: [0]
|
||||
no-with: [0] # handled by no-restricted-syntax
|
||||
object-shorthand: [2, always]
|
||||
one-var-declaration-per-line: [0]
|
||||
one-var: [0]
|
||||
operator-assignment: [2, always]
|
||||
operator-linebreak: [2, after]
|
||||
prefer-arrow-callback: [2, {allowNamedFunctions: true, allowUnboundThis: true}]
|
||||
prefer-const: [2, {destructuring: all, ignoreReadBeforeAssign: true}]
|
||||
prefer-destructuring: [0]
|
||||
prefer-exponentiation-operator: [2]
|
||||
prefer-named-capture-group: [0]
|
||||
prefer-numeric-literals: [2]
|
||||
prefer-object-has-own: [2]
|
||||
prefer-object-spread: [2]
|
||||
prefer-promise-reject-errors: [2, {allowEmptyReject: false}]
|
||||
prefer-regex-literals: [2]
|
||||
prefer-rest-params: [2]
|
||||
prefer-spread: [2]
|
||||
prefer-template: [2]
|
||||
radix: [2, as-needed]
|
||||
regexp/confusing-quantifier: [2]
|
||||
regexp/control-character-escape: [2]
|
||||
regexp/hexadecimal-escape: [0]
|
||||
regexp/letter-case: [0]
|
||||
regexp/match-any: [2]
|
||||
regexp/negation: [2]
|
||||
regexp/no-contradiction-with-assertion: [0]
|
||||
regexp/no-control-character: [0]
|
||||
regexp/no-dupe-characters-character-class: [2]
|
||||
regexp/no-dupe-disjunctions: [2]
|
||||
regexp/no-empty-alternative: [2]
|
||||
regexp/no-empty-capturing-group: [2]
|
||||
regexp/no-empty-character-class: [0]
|
||||
regexp/no-empty-group: [2]
|
||||
regexp/no-empty-lookarounds-assertion: [2]
|
||||
regexp/no-empty-string-literal: [2]
|
||||
regexp/no-escape-backspace: [2]
|
||||
regexp/no-extra-lookaround-assertions: [0]
|
||||
regexp/no-invalid-regexp: [2]
|
||||
regexp/no-invisible-character: [2]
|
||||
regexp/no-lazy-ends: [2]
|
||||
regexp/no-legacy-features: [2]
|
||||
regexp/no-misleading-capturing-group: [0]
|
||||
regexp/no-misleading-unicode-character: [0]
|
||||
regexp/no-missing-g-flag: [2]
|
||||
regexp/no-non-standard-flag: [2]
|
||||
regexp/no-obscure-range: [2]
|
||||
regexp/no-octal: [2]
|
||||
regexp/no-optional-assertion: [2]
|
||||
regexp/no-potentially-useless-backreference: [2]
|
||||
regexp/no-standalone-backslash: [2]
|
||||
regexp/no-super-linear-backtracking: [0]
|
||||
regexp/no-super-linear-move: [0]
|
||||
regexp/no-trivially-nested-assertion: [2]
|
||||
regexp/no-trivially-nested-quantifier: [2]
|
||||
regexp/no-unused-capturing-group: [0]
|
||||
regexp/no-useless-assertions: [2]
|
||||
regexp/no-useless-backreference: [2]
|
||||
regexp/no-useless-character-class: [2]
|
||||
regexp/no-useless-dollar-replacements: [2]
|
||||
regexp/no-useless-escape: [2]
|
||||
regexp/no-useless-flag: [2]
|
||||
regexp/no-useless-lazy: [2]
|
||||
regexp/no-useless-non-capturing-group: [2]
|
||||
regexp/no-useless-quantifier: [2]
|
||||
regexp/no-useless-range: [2]
|
||||
regexp/no-useless-set-operand: [2]
|
||||
regexp/no-useless-string-literal: [2]
|
||||
regexp/no-useless-two-nums-quantifier: [2]
|
||||
regexp/no-zero-quantifier: [2]
|
||||
regexp/optimal-lookaround-quantifier: [2]
|
||||
regexp/optimal-quantifier-concatenation: [0]
|
||||
regexp/prefer-character-class: [0]
|
||||
regexp/prefer-d: [0]
|
||||
regexp/prefer-escape-replacement-dollar-char: [0]
|
||||
regexp/prefer-lookaround: [0]
|
||||
regexp/prefer-named-backreference: [0]
|
||||
regexp/prefer-named-capture-group: [0]
|
||||
regexp/prefer-named-replacement: [0]
|
||||
regexp/prefer-plus-quantifier: [2]
|
||||
regexp/prefer-predefined-assertion: [2]
|
||||
regexp/prefer-quantifier: [0]
|
||||
regexp/prefer-question-quantifier: [2]
|
||||
regexp/prefer-range: [2]
|
||||
regexp/prefer-regexp-exec: [2]
|
||||
regexp/prefer-regexp-test: [2]
|
||||
regexp/prefer-result-array-groups: [0]
|
||||
regexp/prefer-set-operation: [2]
|
||||
regexp/prefer-star-quantifier: [2]
|
||||
regexp/prefer-unicode-codepoint-escapes: [2]
|
||||
regexp/prefer-w: [0]
|
||||
regexp/require-unicode-regexp: [0]
|
||||
regexp/simplify-set-operations: [2]
|
||||
regexp/sort-alternatives: [0]
|
||||
regexp/sort-character-class-elements: [0]
|
||||
regexp/sort-flags: [0]
|
||||
regexp/strict: [2]
|
||||
regexp/unicode-escape: [0]
|
||||
regexp/use-ignore-case: [0]
|
||||
require-atomic-updates: [0]
|
||||
require-await: [0] # handled by @typescript-eslint/require-await
|
||||
require-unicode-regexp: [0]
|
||||
require-yield: [2]
|
||||
sonarjs/cognitive-complexity: [0]
|
||||
sonarjs/elseif-without-else: [0]
|
||||
sonarjs/max-switch-cases: [0]
|
||||
sonarjs/no-all-duplicated-branches: [2]
|
||||
sonarjs/no-collapsible-if: [0]
|
||||
sonarjs/no-collection-size-mischeck: [2]
|
||||
sonarjs/no-duplicate-string: [0]
|
||||
sonarjs/no-duplicated-branches: [0]
|
||||
sonarjs/no-element-overwrite: [2]
|
||||
sonarjs/no-empty-collection: [2]
|
||||
sonarjs/no-extra-arguments: [2]
|
||||
sonarjs/no-gratuitous-expressions: [2]
|
||||
sonarjs/no-identical-conditions: [2]
|
||||
sonarjs/no-identical-expressions: [2]
|
||||
sonarjs/no-identical-functions: [2, 5]
|
||||
sonarjs/no-ignored-return: [2]
|
||||
sonarjs/no-inverted-boolean-check: [2]
|
||||
sonarjs/no-nested-switch: [0]
|
||||
sonarjs/no-nested-template-literals: [0]
|
||||
sonarjs/no-one-iteration-loop: [2]
|
||||
sonarjs/no-redundant-boolean: [2]
|
||||
sonarjs/no-redundant-jump: [2]
|
||||
sonarjs/no-same-line-conditional: [2]
|
||||
sonarjs/no-small-switch: [0]
|
||||
sonarjs/no-unused-collection: [2]
|
||||
sonarjs/no-use-of-empty-return-value: [2]
|
||||
sonarjs/no-useless-catch: [2]
|
||||
sonarjs/non-existent-operator: [2]
|
||||
sonarjs/prefer-immediate-return: [0]
|
||||
sonarjs/prefer-object-literal: [0]
|
||||
sonarjs/prefer-single-boolean-return: [0]
|
||||
sonarjs/prefer-while: [2]
|
||||
sort-imports: [0]
|
||||
sort-keys: [0]
|
||||
sort-vars: [0]
|
||||
strict: [0]
|
||||
symbol-description: [2]
|
||||
unicode-bom: [2, never]
|
||||
unicorn/better-regex: [0]
|
||||
unicorn/catch-error-name: [0]
|
||||
unicorn/consistent-destructuring: [2]
|
||||
unicorn/consistent-empty-array-spread: [2]
|
||||
unicorn/consistent-function-scoping: [2]
|
||||
unicorn/custom-error-definition: [0]
|
||||
unicorn/empty-brace-spaces: [2]
|
||||
unicorn/error-message: [0]
|
||||
unicorn/escape-case: [0]
|
||||
unicorn/expiring-todo-comments: [0]
|
||||
unicorn/explicit-length-check: [0]
|
||||
unicorn/filename-case: [0]
|
||||
unicorn/import-index: [0]
|
||||
unicorn/import-style: [0]
|
||||
unicorn/new-for-builtins: [2]
|
||||
unicorn/no-abusive-eslint-disable: [0]
|
||||
unicorn/no-anonymous-default-export: [0]
|
||||
unicorn/no-array-callback-reference: [0]
|
||||
unicorn/no-array-for-each: [2]
|
||||
unicorn/no-array-method-this-argument: [2]
|
||||
unicorn/no-array-push-push: [2]
|
||||
unicorn/no-array-reduce: [2]
|
||||
unicorn/no-await-expression-member: [0]
|
||||
unicorn/no-await-in-promise-methods: [2]
|
||||
unicorn/no-console-spaces: [0]
|
||||
unicorn/no-document-cookie: [2]
|
||||
unicorn/no-empty-file: [2]
|
||||
unicorn/no-for-loop: [0]
|
||||
unicorn/no-hex-escape: [0]
|
||||
unicorn/no-instanceof-array: [0]
|
||||
unicorn/no-invalid-fetch-options: [2]
|
||||
unicorn/no-invalid-remove-event-listener: [2]
|
||||
unicorn/no-keyword-prefix: [0]
|
||||
unicorn/no-length-as-slice-end: [2]
|
||||
unicorn/no-lonely-if: [2]
|
||||
unicorn/no-magic-array-flat-depth: [0]
|
||||
unicorn/no-negated-condition: [0]
|
||||
unicorn/no-negation-in-equality-check: [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: [0]
|
||||
unicorn/no-process-exit: [0]
|
||||
unicorn/no-single-promise-in-promise-methods: [2]
|
||||
unicorn/no-static-only-class: [2]
|
||||
unicorn/no-thenable: [2]
|
||||
unicorn/no-this-assignment: [2]
|
||||
unicorn/no-typeof-undefined: [2]
|
||||
unicorn/no-unnecessary-await: [2]
|
||||
unicorn/no-unnecessary-polyfills: [2]
|
||||
unicorn/no-unreadable-array-destructuring: [0]
|
||||
unicorn/no-unreadable-iife: [2]
|
||||
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-blob-reading-methods: [2]
|
||||
unicorn/prefer-code-point: [0]
|
||||
unicorn/prefer-date-now: [2]
|
||||
unicorn/prefer-default-parameters: [0]
|
||||
unicorn/prefer-dom-node-append: [2]
|
||||
unicorn/prefer-dom-node-dataset: [0]
|
||||
unicorn/prefer-dom-node-remove: [2]
|
||||
unicorn/prefer-dom-node-text-content: [2]
|
||||
unicorn/prefer-event-target: [2]
|
||||
unicorn/prefer-export-from: [0]
|
||||
unicorn/prefer-includes: [2]
|
||||
unicorn/prefer-json-parse-buffer: [0]
|
||||
unicorn/prefer-keyboard-event-key: [2]
|
||||
unicorn/prefer-logical-operator-over-ternary: [2]
|
||||
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-protocol: [2]
|
||||
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: [2]
|
||||
unicorn/prefer-reflect-apply: [0]
|
||||
unicorn/prefer-regexp-test: [2]
|
||||
unicorn/prefer-set-has: [0]
|
||||
unicorn/prefer-set-size: [2]
|
||||
unicorn/prefer-spread: [0]
|
||||
unicorn/prefer-string-raw: [0]
|
||||
unicorn/prefer-string-replace-all: [0]
|
||||
unicorn/prefer-string-slice: [0]
|
||||
unicorn/prefer-string-starts-ends-with: [2]
|
||||
unicorn/prefer-string-trim-start-end: [2]
|
||||
unicorn/prefer-structured-clone: [2]
|
||||
unicorn/prefer-switch: [0]
|
||||
unicorn/prefer-ternary: [0]
|
||||
unicorn/prefer-text-content: [2]
|
||||
unicorn/prefer-top-level-await: [0]
|
||||
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/switch-case-braces: [0]
|
||||
unicorn/template-indent: [2]
|
||||
unicorn/text-encoding-identifier-case: [0]
|
||||
unicorn/throw-new-error: [2]
|
||||
use-isnan: [2]
|
||||
valid-typeof: [2, {requireStringLiterals: true}]
|
||||
vars-on-top: [0]
|
||||
wc/attach-shadow-constructor: [2]
|
||||
wc/define-tag-after-class-definition: [0]
|
||||
wc/expose-class-on-global: [0]
|
||||
wc/file-name-matches-element: [2]
|
||||
wc/guard-define-call: [0]
|
||||
wc/guard-super-call: [2]
|
||||
wc/max-elements-per-file: [0]
|
||||
wc/no-child-traversal-in-attributechangedcallback: [2]
|
||||
wc/no-child-traversal-in-connectedcallback: [2]
|
||||
wc/no-closed-shadow-root: [2]
|
||||
wc/no-constructor-attributes: [2]
|
||||
wc/no-constructor-params: [2]
|
||||
wc/no-constructor: [2]
|
||||
wc/no-customized-built-in-elements: [2]
|
||||
wc/no-exports-with-element: [0]
|
||||
wc/no-invalid-element-name: [2]
|
||||
wc/no-invalid-extends: [2]
|
||||
wc/no-method-prefixed-with-on: [2]
|
||||
wc/no-self-class: [2]
|
||||
wc/no-typos: [2]
|
||||
wc/require-listener-teardown: [2]
|
||||
wc/tag-name-matches-class: [2]
|
||||
yoda: [2, never]
|
|
@ -5,7 +5,5 @@
|
|||
/public/assets/img/svg/*.svg linguist-generated
|
||||
/templates/swagger/v1_json.tmpl linguist-generated
|
||||
/vendor/** -text -eol linguist-vendored
|
||||
/web_src/fomantic/build/** linguist-generated
|
||||
/web_src/fomantic/_site/globals/site.variables linguist-language=Less
|
||||
/web_src/js/vendor/** -text -eol linguist-vendored
|
||||
Dockerfile.* linguist-language=Dockerfile
|
||||
|
|
|
@ -13,5 +13,5 @@ contact_links:
|
|||
url: https://docs.gitea.com/help/faq
|
||||
about: Please check if your question isn't mentioned here.
|
||||
- name: Crowdin Translations
|
||||
url: https://crowdin.com/project/gitea
|
||||
url: https://translate.gitea.com
|
||||
about: Translations are managed here.
|
||||
|
|
|
@ -3,3 +3,5 @@ self-hosted-runner:
|
|||
- actuated-4cpu-8gb
|
||||
- actuated-4cpu-16gb
|
||||
- nscloud
|
||||
- namespace-profile-gitea-release-docker
|
||||
- namespace-profile-gitea-release-binary
|
||||
|
|
|
@ -41,7 +41,7 @@ modifies/internal:
|
|||
- ".dockerignore"
|
||||
- "docker/**"
|
||||
- ".editorconfig"
|
||||
- ".eslintrc.yaml"
|
||||
- ".eslintrc.cjs"
|
||||
- ".golangci.yml"
|
||||
- ".gitpod.yml"
|
||||
- ".markdownlint.yaml"
|
||||
|
@ -49,7 +49,7 @@ modifies/internal:
|
|||
- "stylelint.config.js"
|
||||
- ".yamllint.yaml"
|
||||
- ".github/**"
|
||||
- ".gitea/"
|
||||
- ".gitea/**"
|
||||
- ".devcontainer/**"
|
||||
- "build.go"
|
||||
- "build/**"
|
||||
|
@ -73,9 +73,9 @@ modifies/go:
|
|||
modifies/frontend:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- "**/*.js"
|
||||
- "**/*.ts"
|
||||
- "**/*.vue"
|
||||
- "*.js"
|
||||
- "*.ts"
|
||||
- "web_src/**"
|
||||
|
||||
docs-update-needed:
|
||||
- changed-files:
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
name: cron-licenses
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "7 0 * * 1" # every Monday at 00:07 UTC
|
||||
# schedule:
|
||||
# - cron: "7 0 * * 1" # every Monday at 00:07 UTC
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
@ -15,7 +15,7 @@ jobs:
|
|||
with:
|
||||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- run: make generate-license generate-gitignore
|
||||
- run: make generate-gitignore
|
||||
timeout-minutes: 40
|
||||
- name: push translations to repo
|
||||
uses: appleboy/git-push-action@v0.0.3
|
||||
|
|
|
@ -51,14 +51,16 @@ jobs:
|
|||
- "options/locale/locale_en-US.ini"
|
||||
|
||||
frontend:
|
||||
- "**/*.js"
|
||||
- "*.js"
|
||||
- "*.ts"
|
||||
- "web_src/**"
|
||||
- "tools/*.js"
|
||||
- "tools/*.ts"
|
||||
- "assets/emoji.json"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
- "Makefile"
|
||||
- ".eslintrc.yaml"
|
||||
- "stylelint.config.js"
|
||||
- ".eslintrc.cjs"
|
||||
- ".npmrc"
|
||||
|
||||
docs:
|
||||
|
@ -85,6 +87,7 @@ jobs:
|
|||
|
||||
swagger:
|
||||
- "templates/swagger/v1_json.tmpl"
|
||||
- "templates/swagger/v1_input.json"
|
||||
- "Makefile"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
|
|
|
@ -37,7 +37,7 @@ jobs:
|
|||
python-version: "3.12"
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: pip install poetry
|
||||
|
@ -66,7 +66,7 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend
|
||||
|
@ -95,7 +95,7 @@ jobs:
|
|||
go-version-file: go.mod
|
||||
check-latest: true
|
||||
- run: make deps-backend deps-tools
|
||||
- run: make lint-go-windows lint-go-vet
|
||||
- run: make lint-go-windows lint-go-gitea-vet
|
||||
env:
|
||||
TAGS: bindata sqlite sqlite_unlock_notify
|
||||
GOOS: windows
|
||||
|
@ -137,7 +137,7 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend
|
||||
|
@ -186,7 +186,7 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend
|
||||
|
|
|
@ -154,12 +154,15 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
# the bitnami mysql image has more options than the official one, it's easier to customize
|
||||
image: bitnami/mysql:8.0
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: true
|
||||
ALLOW_EMPTY_PASSWORD: true
|
||||
MYSQL_DATABASE: testgitea
|
||||
ports:
|
||||
- "3306:3306"
|
||||
options: >-
|
||||
--mount type=tmpfs,destination=/bitnami/mysql/data
|
||||
elasticsearch:
|
||||
image: elasticsearch:7.5.0
|
||||
env:
|
||||
|
@ -188,7 +191,8 @@ jobs:
|
|||
- name: run migration tests
|
||||
run: make test-mysql-migration
|
||||
- name: run tests
|
||||
run: make integration-test-coverage
|
||||
# run: make integration-test-coverage (at the moment, no coverage is really handled)
|
||||
run: make test-mysql
|
||||
env:
|
||||
TAGS: bindata
|
||||
RACE_ENABLED: true
|
||||
|
@ -198,12 +202,10 @@ jobs:
|
|||
test-mssql:
|
||||
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
|
||||
needs: files-changed
|
||||
# specifying the version of ubuntu in use as mssql fails on newer kernels
|
||||
# pending resolution from vendor
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
mssql:
|
||||
image: mcr.microsoft.com/mssql/server:2017-latest
|
||||
image: mcr.microsoft.com/mssql/server:2019-latest
|
||||
env:
|
||||
ACCEPT_EULA: Y
|
||||
MSSQL_PID: Standard
|
||||
|
|
|
@ -23,7 +23,7 @@ jobs:
|
|||
check-latest: true
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend frontend deps-backend
|
||||
|
|
|
@ -10,7 +10,7 @@ concurrency:
|
|||
|
||||
jobs:
|
||||
nightly-binary:
|
||||
runs-on: nscloud
|
||||
runs-on: namespace-profile-gitea-release-binary
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
|
@ -22,7 +22,7 @@ jobs:
|
|||
check-latest: true
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend deps-backend
|
||||
|
@ -58,7 +58,7 @@ jobs:
|
|||
run: |
|
||||
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
|
||||
nightly-docker-rootful:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
|
@ -95,7 +95,7 @@ jobs:
|
|||
push: true
|
||||
tags: gitea/gitea:${{ steps.clean_name.outputs.branch }}
|
||||
nightly-docker-rootless:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
|
|
|
@ -11,7 +11,7 @@ concurrency:
|
|||
|
||||
jobs:
|
||||
binary:
|
||||
runs-on: nscloud
|
||||
runs-on: namespace-profile-gitea-release-binary
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
|
@ -23,7 +23,7 @@ jobs:
|
|||
check-latest: true
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend deps-backend
|
||||
|
@ -68,7 +68,7 @@ jobs:
|
|||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
docker-rootful:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
|
@ -99,7 +99,7 @@ jobs:
|
|||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
docker-rootless:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
|
|
|
@ -13,7 +13,7 @@ concurrency:
|
|||
|
||||
jobs:
|
||||
binary:
|
||||
runs-on: nscloud
|
||||
runs-on: namespace-profile-gitea-release-binary
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
|
@ -25,7 +25,7 @@ jobs:
|
|||
check-latest: true
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
cache: npm
|
||||
cache-dependency-path: package-lock.json
|
||||
- run: make deps-frontend deps-backend
|
||||
|
@ -70,7 +70,7 @@ jobs:
|
|||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
docker-rootful:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
|
@ -88,9 +88,9 @@ jobs:
|
|||
# 1.2
|
||||
# 1.2.3
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{version}}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
|
@ -105,7 +105,7 @@ jobs:
|
|||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
docker-rootless:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: namespace-profile-gitea-release-docker
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
|
||||
|
@ -126,9 +126,9 @@ jobs:
|
|||
# 1.2
|
||||
# 1.2.3
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{version}}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
|
|
|
@ -9,6 +9,11 @@ _test
|
|||
|
||||
# IntelliJ
|
||||
.idea
|
||||
.run
|
||||
|
||||
# IntelliJ Gateway
|
||||
.uuid
|
||||
|
||||
# Goland's output filename can not be set manually
|
||||
/go_build_*
|
||||
/gitea_*
|
||||
|
@ -28,6 +33,7 @@ _testmain.go
|
|||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
*.tsbuildinfo
|
||||
|
||||
*coverage.out
|
||||
coverage.all
|
||||
|
@ -78,18 +84,6 @@ cpu.out
|
|||
/public/assets/fonts
|
||||
/public/assets/licenses.txt
|
||||
/vendor
|
||||
/web_src/fomantic/node_modules
|
||||
/web_src/fomantic/build/*
|
||||
!/web_src/fomantic/build/semantic.js
|
||||
!/web_src/fomantic/build/semantic.css
|
||||
!/web_src/fomantic/build/themes
|
||||
/web_src/fomantic/build/themes/*
|
||||
!/web_src/fomantic/build/themes/default
|
||||
/web_src/fomantic/build/themes/default/assets/*
|
||||
!/web_src/fomantic/build/themes/default/assets/fonts
|
||||
/web_src/fomantic/build/themes/default/assets/fonts/*
|
||||
!/web_src/fomantic/build/themes/default/assets/fonts/icons.woff2
|
||||
!/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
|
||||
/VERSION
|
||||
/.air
|
||||
/.go-licenses
|
||||
|
|
|
@ -19,10 +19,12 @@ linters:
|
|||
- revive
|
||||
- staticcheck
|
||||
- stylecheck
|
||||
- testifylint
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unused
|
||||
- unparam
|
||||
- usetesting
|
||||
- wastedassign
|
||||
|
||||
run:
|
||||
|
@ -34,6 +36,10 @@ output:
|
|||
show-stats: true
|
||||
|
||||
linters-settings:
|
||||
testifylint:
|
||||
disable:
|
||||
- go-require
|
||||
- require-error
|
||||
stylecheck:
|
||||
checks: ["all", "-ST1005", "-ST1003"]
|
||||
nakedret:
|
||||
|
@ -96,6 +102,8 @@ linters-settings:
|
|||
desc: do not use the ini package, use gitea's config system instead
|
||||
- pkg: gitea.com/go-chi/cache
|
||||
desc: do not use the go-chi cache package, use gitea's cache system
|
||||
usetesting:
|
||||
os-temp-dir: true
|
||||
|
||||
issues:
|
||||
max-issues-per-linter: 0
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Unknwon <u@gogs.io> <joe2010xtmf@163.com>
|
||||
Unknwon <u@gogs.io> 无闻 <u@gogs.io>
|
926
CHANGELOG.md
926
CHANGELOG.md
|
@ -4,6 +4,932 @@ This changelog goes through the changes that have been made in each release
|
|||
without substantial changes to our git log; to see the highlights of what has
|
||||
been added to each release, please refer to the [blog](https://blog.gitea.com).
|
||||
|
||||
## [1.23.5](https://github.com/go-gitea/gitea/releases/tag/v1.23.5) - 2025-03-04
|
||||
|
||||
* SECURITY
|
||||
* Bump x/oauth2 & x/crypto (#33704) (#33727)
|
||||
* PERFORMANCE
|
||||
* Optimize user dashboard loading (#33686) (#33708)
|
||||
* BUGFIXES
|
||||
* Fix navbar dropdown item align (#33782)
|
||||
* Fix inconsistent closed issue list icon (#33722) (#33728)
|
||||
* Fix for Maven Package Naming Convention Handling (#33678) (#33679)
|
||||
* Improve Open-with URL encoding (#33666) (#33680)
|
||||
* Deleting repository should unlink all related packages (#33653) (#33673)
|
||||
* Fix omitempty bug (#33663) (#33670)
|
||||
* Upgrade go-crypto from 1.1.4 to 1.1.6 (#33745) (#33754)
|
||||
* Fix OCI image.version annotation for releases to use full semver (#33698) (#33701)
|
||||
* Try to fix ACME path when renew (#33668) (#33693)
|
||||
* Fix mCaptcha bug (#33659) (#33661)
|
||||
* Git graph: don't show detached commits (#33645) (#33650)
|
||||
* Use MatchPhraseQuery for bleve code search (#33628)
|
||||
* Adjust appearence of commit status webhook (#33778) #33789
|
||||
* Upgrade golang net from 0.35.0 -> 0.36.0 (#33795) #33796
|
||||
|
||||
## [1.23.4](https://github.com/go-gitea/gitea/releases/tag/v1.23.4) - 2025-02-16
|
||||
|
||||
* SECURITY
|
||||
* Enhance routers for the Actions variable operations (#33547) (#33553)
|
||||
* Enhance routers for the Actions runner operations (#33549) (#33555)
|
||||
* Fix project issues list and counting (#33594) #33619
|
||||
* PERFORMANCES
|
||||
* Performance optimization for pull request files loading comments attachments (#33585) (#33592)
|
||||
* BUGFIXES
|
||||
* Add a transaction to `pickTask` (#33543) (#33563)
|
||||
* Fix mirror bug (#33597) (#33607)
|
||||
* Use default Git timeout when checking repo health (#33593) (#33598)
|
||||
* Fix PR's target branch dropdown (#33589) (#33591)
|
||||
* Fix various problems (artifact order, api empty slice, assignee check, fuzzy prompt, mirror proxy, adopt git) (#33569) (#33577)
|
||||
* Rework suggestion backend (#33538) (#33546)
|
||||
* Fix context usage (#33554) (#33557)
|
||||
* Only show the latest version in the Arch index (#33262) (#33580)
|
||||
* Skip deletion error for action artifacts (#33476) (#33568)
|
||||
* Make actions URL in commit status webhooks absolute (#33620) #33632
|
||||
* Add missing locale (#33641) #33642
|
||||
|
||||
## [1.23.3](https://github.com/go-gitea/gitea/releases/tag/v1.23.3) - 2025-02-06
|
||||
|
||||
* Security
|
||||
* Build Gitea with Golang v1.23.6 to fix security bugs
|
||||
* BUGFIXES
|
||||
* Fix a bug caused by status webhook template #33512
|
||||
|
||||
## [1.23.2](https://github.com/go-gitea/gitea/releases/tag/1.23.2) - 2025-02-04
|
||||
|
||||
* BREAKING
|
||||
* Add tests for webhook and fix some webhook bugs (#33396) (#33442)
|
||||
* Package webhook’s Organization was incorrectly used as the User struct. This PR fixes the issue.
|
||||
* This changelog is just a hint. The change is not really breaking because most fields are the same, most users are not affected.
|
||||
* ENHANCEMENTS
|
||||
* Clone button enhancements (#33362) (#33404)
|
||||
* Repo homepage styling tweaks (#33289) (#33381)
|
||||
* Add a confirm dialog for "sync fork" (#33270) (#33273)
|
||||
* Make tracked time representation display as hours (#33315) (#33334)
|
||||
* Improve sync fork behavior (#33319) (#33332)
|
||||
* BUGFIXES
|
||||
* Fix code button alignment (#33345) (#33351)
|
||||
* Correct bot label `vertical-align` (#33477) (#33480)
|
||||
* Fix SSH LFS memory usage (#33455) (#33460)
|
||||
* Fix issue sidebar dropdown keyboard support (#33447) (#33450)
|
||||
* Fix user avatar (#33439)
|
||||
* Fix `GetCommitBranchStart` bug (#33298) (#33421)
|
||||
* Add pubdate for repository rss and add some tests (#33411) (#33416)
|
||||
* Add missed auto merge feed message on dashboard (#33309) (#33405)
|
||||
* Fix issue suggestion bug (#33389) (#33391)
|
||||
* Make issue suggestion work for all editors (#33340) (#33342)
|
||||
* Fix issue count (#33338) (#33341)
|
||||
* Fix Account linking page (#33325) (#33327)
|
||||
* Fix closed dependency title (#33285) (#33287)
|
||||
* Fix sidebar milestone link (#33269) (#33272)
|
||||
* Fix missing license when sync mirror (#33255) (#33258)
|
||||
* Fix upload file form (#33230) (#33233)
|
||||
* Fix mirror bug (#33224) (#33225)
|
||||
* Fix system admin cannot fork or get private fork with API (#33401) (#33417)
|
||||
* Fix push message behavior (#33215) (#33317)
|
||||
* Trivial fixes (#33304) (#33312)
|
||||
* Fix "stop time tracking button" on navbar (#33084) (#33300)
|
||||
* Fix tag route and empty repo (#33253)
|
||||
* Fix cache test triggered by non memory cache (#33220) (#33221)
|
||||
* Revert empty lfs ref name (#33454) (#33457)
|
||||
* Fix flex width (#33414) (#33418)
|
||||
* Fix commit status events (#33320) #33493
|
||||
* Fix unnecessary comment when moving issue on the same project column (#33496) #33499
|
||||
* Add timetzdata build tag to binary releases (#33463) #33503
|
||||
* MISC
|
||||
* Use ProtonMail/go-crypto to replace keybase/go-crypto (#33402) (#33410)
|
||||
* Update katex to latest version (#33361)
|
||||
* Update go tool dependencies (#32916) (#33355)
|
||||
|
||||
## [1.23.1](https://github.com/go-gitea/gitea/releases/tag/v1.23.1) - 2025-01-09
|
||||
|
||||
* ENHANCEMENTS
|
||||
* Move repo size to sidebar (#33155) (#33182)
|
||||
* BUGFIXES
|
||||
* Use updated path to s6-svscan after alpine upgrade (#33185) (#33188)
|
||||
* Fix fuzz test (#33156) (#33158)
|
||||
* Fix raw file API ref handling (#33172) (#33189)
|
||||
* Fix ACME panic (#33178) (#33186)
|
||||
* Fix branch dropdown not display ref name (#33159) (#33183)
|
||||
* Fix assignee list overlapping in Issue sidebar (#33176) (#33181)
|
||||
* Fix sync fork for consistency (#33147) #33192
|
||||
* Fix editor markdown not incrementing in a numbered list (#33187) #33193
|
||||
|
||||
## [1.23.0](https://github.com/go-gitea/gitea/releases/tag/v1.23.0) - 2025-01-08
|
||||
|
||||
* BREAKING
|
||||
* Rename config option `[camo].Allways` to `[camo].Always` (#32097)
|
||||
* Remove SHA1 for support for ssh rsa signing (#31857)
|
||||
* Use UTC as default timezone when schedule Actions cron tasks (#31742)
|
||||
* Delete Actions logs older than 1 year by default (#31735)
|
||||
* Make OIDC introspection authentication strictly require Client ID and secret (#31632)
|
||||
|
||||
* SECURITY
|
||||
* Include file extension checks in attachment API (#32151)
|
||||
* Include all security fixes which have been backported to v1.22
|
||||
|
||||
* FEATURES
|
||||
* Allow to fork repository into the same owner (#32819)
|
||||
* Support "merge upstream branch" (Sync fork) (#32741)
|
||||
* Add Arch package registry (#32692)
|
||||
* Allow to disable the password-based login (sign-in) form (#32687)
|
||||
* Allow cropping an avatar before setting it (#32565)
|
||||
* Support quote selected comments to reply (#32431)
|
||||
* Add reviewers selection to new pull request (#32403)
|
||||
* Suggestions for issues (#32327)
|
||||
* Add priority to protected branch (#32286)
|
||||
* Included tag search capabilities (#32045)
|
||||
* Add option to filter board cards by labels and assignees (#31999)
|
||||
* Add automatic light/dark option for the colorblind theme (#31997)
|
||||
* Support migration from AWS CodeCommit (#31981)
|
||||
* Introduce globallock as distributed locks (#31908 & #31813)
|
||||
* Support compression for Actions logs & enable by default (#31761 & #32013)
|
||||
* Add pure SSH LFS support (#31516)
|
||||
* Add Passkey login support (#31504)
|
||||
* Actions support workflow dispatch event (#28163)
|
||||
* Support repo license (#24872)
|
||||
* Issue time estimate, meaningful time tracking (#23113)
|
||||
* GitHub like repo home page (#32213 & #32847)
|
||||
* Rearrange Clone Panel (#31142)
|
||||
* Enhancing Gitea OAuth2 Provider with Granular Scopes for Resource Access (#32573)
|
||||
* Use env GITEA_RUNNER_REGISTRATION_TOKEN as global runner token (#32946) #32964
|
||||
* Update i18n.go - Language Picker (#32933) #32935
|
||||
|
||||
* PERFORMANCE
|
||||
* Perf: add extra index to notification table (#32395)
|
||||
* Introduce OrgList and add LoadTeams, optimaze Load teams for orgs (#32543)
|
||||
* Improve performance of diffs (#32393)
|
||||
* Make LFS http_client parallel within a batch. (#32369)
|
||||
* Add new index for action to resolve the performance problem (#32333)
|
||||
* Improve get feed with pagination (#31821)
|
||||
* Performance improvements for pull request list API (#30490)
|
||||
* Use batch database operations instead of one by one to optimze api pulls (#32680)
|
||||
* Use gitrepo.GetTreePathLatestCommit to get file lastest commit instead from latest commit cache (#32987) #33046
|
||||
|
||||
* ENHANCEMENTS
|
||||
* Code
|
||||
* Remove unnecessary border in repo home page sidebar (#32767)
|
||||
* Add 'Copy path' button to file view (#32584)
|
||||
* Improve diff file tree (#32658)
|
||||
* Add new [lfs_client].BATCH_SIZE and [server].LFS_MAX_BATCH_SIZE config settings. (#32307)
|
||||
* Updated tokenizer to better matching when search for code snippets (#32261)
|
||||
* Change the code search to sort results by relevance (#32134)
|
||||
* Support migrating GitHub/GitLab PR draft status (#32242)
|
||||
* Move lock icon position and add additional tooltips to branch list page (#31839)
|
||||
* Add tag name in the commits list (#31082)
|
||||
* Add `MAX_ROWS` option for CSV rendering (#30268)
|
||||
* Allow code search by filename (#32210)
|
||||
* Make git push options accept short name (#32245)
|
||||
* Repo file list enhancements (#32835)
|
||||
|
||||
* Markdown & Editor
|
||||
* Refactor markdown math render, add dollor-backquote syntax support (#32831)
|
||||
* Make Monaco theme follow browser, fully type codeeditor.ts (#32756)
|
||||
* Refactor markdown editor and use it for milestone description editor (#32688)
|
||||
* Add some handy markdown editor features (#32400)
|
||||
* Improve markdown textarea for indentation and lists (#31406)
|
||||
|
||||
* Issue
|
||||
* Add label/author/assignee filters to the user/org home issue list (#32779)
|
||||
* Refactor issue filter (labels, poster, assignee) (#32771)
|
||||
* Style unification for the issue_management area (#32605)
|
||||
* Add "View all branches/tags" entry to Branch Selector (#32653)
|
||||
* Improve textarea paste (#31948)
|
||||
* Add avif image file support (#32508)
|
||||
* Prevent from submitting issue/comment on uploading (#32263)
|
||||
* Issue Templates: add option to have dropdown printed list (#31577)
|
||||
* Allow searching issues by ID (#31479)
|
||||
* Add `is_archived` option for issue indexer (#32735)
|
||||
* Improve attachment upload methods (#30513)
|
||||
* Support issue template assignees (#31083)
|
||||
* Prevent simultaneous editing of comments and issues (#31053)
|
||||
* Add issue comment when moving issues from one column to another of the project (#29311)
|
||||
|
||||
* Pull Request
|
||||
* Display head branch more comfortable on pull request view (#32000)
|
||||
* Simplify review UI (#31062)
|
||||
* Allow force push to protected branches (#28086)
|
||||
* Add line-through for deleted branch on pull request view page (#32500)
|
||||
* Support requested_reviewers data in comment webhook events (#26178)
|
||||
* Allow maintainers to view and edit files of private repos when "Allow maintainers to edit" is enabled (#32215)
|
||||
* Allow including `Reviewed-on`/`Reviewed-by` lines for custom merge messages (#31211)
|
||||
|
||||
* Actions
|
||||
* Render job title as commit message (#32748)
|
||||
* Refactor RepoActionView.vue, add `::group::` support (#32713)
|
||||
* Make RepoActionView.vue support `##[group]` (#32770)
|
||||
* Support `pull_request_target` event for commit status (#31703)
|
||||
* Detect whether action view branch was deleted (#32764)
|
||||
* Allow users with write permission to run actions (#32644)
|
||||
* Show latest run when visit /run/latest (#31808)
|
||||
|
||||
* Packages
|
||||
* Improve rubygems package registry (#31357)
|
||||
* Add support for npm bundleDependencies (#30751)
|
||||
* Add signature support for the RPM module (#27069)
|
||||
* Extract and display readme and comments for Composer packages (#30927)
|
||||
|
||||
* Project
|
||||
* Add title to project view page (#32747)
|
||||
* Set the columns height to hug all its contents (#31726)
|
||||
* Rename project `board` -> `column` to make the UI less confusing (#30170)
|
||||
|
||||
* User & Organazition
|
||||
* Use better name for userinfo structure (#32544)
|
||||
* Use user.FullName in Oauth2 id_token response (#32542)
|
||||
* Limit org member view of restricted users (#32211)
|
||||
* Allow disabling authentication related user features (#31535)
|
||||
* Add option to change mail from user display name (#31528)
|
||||
* Use FullName in Emails to address the recipient if possible (#31527)
|
||||
|
||||
* Administration
|
||||
* Add support for a credentials chain for minio access (#31051)
|
||||
* Move admin routers from /admin to /-/admin (#32189)
|
||||
* Add cache test for admins (#31265)
|
||||
* Add option for mailer to override mail headers (#27860)
|
||||
* Azure blob storage support (#30995)
|
||||
* Supports forced use of S3 virtual-hosted style (#30969)
|
||||
* Move repository visibility to danger zone in the settings area (#31126)
|
||||
|
||||
* Others
|
||||
* Remove urls from translations (#31950)
|
||||
* Simplify 404/500 page (#31409)
|
||||
* Optimize installation-page experience (#32558)
|
||||
* Refactor login page (#31530)
|
||||
* Add new event commit status creation and webhook implementation (#27151)
|
||||
* Repo Activity: count new issues that were closed (#31776)
|
||||
* Set manual `tabindex`es on login page (#31689)
|
||||
* Add `YEAR`, `MONTH`, `MONTH_ENGLISH`, `DAY` variables for template repos (#31584)
|
||||
* Add typescript guideline and typescript-specific eslint plugins and fix issues (#31521)
|
||||
* Make toast support preventDuplicates (#31501)
|
||||
* Fix tautological conditions (#30735)
|
||||
* Issue change title notifications (#33050) #33065
|
||||
|
||||
* API
|
||||
* Implement update branch API (#32433)
|
||||
* Fix missing outputs for jobs with matrix (#32823)
|
||||
* Make API "compare" accept commit IDs (#32801)
|
||||
* Add github compatible tarball download API endpoints (#32572)
|
||||
* Harden runner updateTask and updateLog api (#32462)
|
||||
* Add `DISABLE_ORGANIZATIONS_PAGE` and `DISABLE_CODE_PAGE` settings for explore pages and fix an issue related to user search (#32288)
|
||||
* Make admins adhere to branch protection rules (#32248)
|
||||
* Calculate `PublicOnly` for org membership only once (#32234)
|
||||
* Allow filtering PRs by poster in the ListPullRequests API (#32209)
|
||||
* Return 404 instead of error when commit not exist (#31977)
|
||||
* Save initial signup information for users to aid in spam prevention (#31852)
|
||||
* Fix upload maven pacakge parallelly (#31851)
|
||||
* Fix null requested_reviewer from API (#31773)
|
||||
* Add permission description for API to add repo collaborator (#31744)
|
||||
* Add return type to GetRawFileOrLFS and GetRawFile (#31680)
|
||||
* Add skip secondary authorization option for public oauth2 clients (#31454)
|
||||
* Add tag protection via rest api #17862 (#31295)
|
||||
* Document possible action types for the user activity feed API (#31196)
|
||||
* Add topics for repository API (#31127)
|
||||
* Add support for searching users by email (#30908)
|
||||
* Add API endpoints for getting action jobs status (#26673)
|
||||
|
||||
* REFACTOR
|
||||
* Update JS and PY dependencies (#31940)
|
||||
* Enable `no-jquery/no-parse-html-literal` and fix violation (#31684)
|
||||
* Refactor image diff (#31444)
|
||||
* Refactor CSRF token (#32216)
|
||||
* Fix some typescript issues (#32586)
|
||||
* Refactor names (#31405)
|
||||
* Use per package global lock for container uploads instead of memory lock (#31860)
|
||||
* Move team related functions to service layer (#32537)
|
||||
* Move GetFeeds to service layer (#32526)
|
||||
* Resolve lint for unused parameter and unnecessary type arguments (#30750)
|
||||
* Reimplement GetUserOrgsList to make it simple and clear (#32486)
|
||||
* Move some functions from issue.go to standalone files (#32468)
|
||||
* Refactor sidebar assignee&milestone&project selectors (#32465)
|
||||
* Refactor sidebar label selector (#32460)
|
||||
* Fix a number of typescript issues (#32459)
|
||||
* Refactor language menu and dom utils (#32450)
|
||||
* Refactor issue page info (#32445)
|
||||
* Split issue sidebar into small templates (#32444)
|
||||
* Refactor template ctx and render utils (#32422)
|
||||
* Refactor repo legacy (#32404)
|
||||
* Refactor markup package (#32399)
|
||||
* Refactor markup render system (#32533 & #32589 & #32612)
|
||||
* Refactor the DB migration system slightly (#32344)
|
||||
* Remove jQuery import from some files (#32512)
|
||||
* Strict pagination check (#32548)
|
||||
* Split mail sender sub package from mailer service package (#32618)
|
||||
* Remove outdated code about fixture generation (#32708)
|
||||
* Refactor RepoBranchTagSelector (#32681)
|
||||
* Refactor issue list (#32755)
|
||||
* Refactor LabelEdit (#32752)
|
||||
* Split issue/pull view router function as multiple smaller functions (#32749)
|
||||
* Refactor some LDAP code (#32849)
|
||||
* Unify repo search order by logic (#30876)
|
||||
* Remove duplicate empty repo check in delete branch API (#32569)
|
||||
* Replace deprecated `math/rand` functions (#30733)
|
||||
* Remove fomantic dimmer module (#30723)
|
||||
* Add types to fetch,toast,bootstrap,svg (#31627)
|
||||
* Refactor webhook (#31587)
|
||||
* Move AddCollabrator and CreateRepositoryByExample to service layer (#32419)
|
||||
* Refactor RepoRefByType (#32413)
|
||||
* Refactor: remove redundant err declarations (#32381)
|
||||
* Refactor markup code (#31399)
|
||||
* Refactor render system (orgmode) (#32671)
|
||||
* Refactor render system (#32492)
|
||||
* Refactor markdown render (#32736 & #32728)
|
||||
* Refactor repo unit "disabled" check (#31389)
|
||||
* Refactor route path normalization (#31381)
|
||||
* Refactor to use UnsafeStringToBytes (#31358)
|
||||
* Migrate vue components to setup (#32329)
|
||||
* Refactor globallock (#31933)
|
||||
* Use correct function name (#31887)
|
||||
* Use a common message template instead of a special one (#31878)
|
||||
* Fix a number of Typescript issues (#31877)
|
||||
* Refactor dropzone (#31482)
|
||||
* Move custom `tw-` helpers to tailwind plugin (#31184)
|
||||
* Replace `gt-word-break` with `tw-break-anywhere` (#31183)
|
||||
* Drop `IDOrderDesc` for listing Actions task and always order by `id DESC` (#31150)
|
||||
* Split common-global.js into separate files (#31438)
|
||||
* Improve detecting empty files (#31332)
|
||||
* Use `querySelector` over alternative DOM methods (#31280)
|
||||
* Remove jQuery `.text()` (#30506)
|
||||
* Use repo as of renderctx's member rather than a repoPath on metas (#29222)
|
||||
* Refactor some frontend problems (#32646)
|
||||
* Refactor DateUtils and merge TimeSince (#32409)
|
||||
* Replace DateTime with proper functions (#32402)
|
||||
* Replace DateTime with DateUtils (#32383)
|
||||
* Convert frontend code to typescript (#31559)
|
||||
* Refactor maven package registry (#33049) #33057
|
||||
* Refactor testfixtures #33028
|
||||
|
||||
* BUGFIXES
|
||||
* Fix issues with inconsistent spacing in areas (#32607)
|
||||
* Fix incomplete Actions status aggregations (#32859)
|
||||
* In some lfs server implementations, they require the ref attribute. (#32838)
|
||||
* Update the list of watchers and stargazers when clicking watch/unwatch or star/unstar (#32570)
|
||||
* Fix `recentupdate` sorting bugs (#32505)
|
||||
* Fix incorrect "Target branch does not exist" in PR title (#32222)
|
||||
* Handle "close" actionable references for manual merges (#31879)
|
||||
* render plain text file if the LFS object doesn't exist (#31812)
|
||||
* Fix Null Pointer error for CommitStatusesHideActionsURL (#31731)
|
||||
* Fix loadRepository error when access user dashboard (#31719)
|
||||
* Hide the "Details" link of commit status when the user cannot access actions (#30156)
|
||||
* Fix duplicate dropdown dividers (#32760)
|
||||
* Fix SSPI button visibility when SSPI is the only enabled method (#32841)
|
||||
* Fix overflow on org header (#32837)
|
||||
* Exclude protected branches from recently pushed (#31748)
|
||||
* Fix large image overflow in comment page (#31740)
|
||||
* Fix milestone deadline and date related problems (#32339)
|
||||
* Fix markdown preview $$ support (#31514)
|
||||
* Fix a compilation error in the Gitpod environment (#32559)
|
||||
* Fix PR diff review form submit (#32596)
|
||||
* Fix a number of typescript issues (#32308)
|
||||
* Fix some function names in comment (#32300)
|
||||
* Fix absolute-date (#32375)
|
||||
* Clarify Actions resources ownership (#31724)
|
||||
* Try to fix ACME directory problem (#33072) #33077
|
||||
* Inherit submodules from template repository content (#16237) #33068
|
||||
* Use project's redirect url instead of composing url (#33058) #33064
|
||||
* Fix toggle commit body button ui when latest commit message is long (#32997) #33034
|
||||
* Fix package error handling and npm meta and empty repo guide #33112
|
||||
* Fix empty git repo handling logic and fix mobile view (#33101) #33102
|
||||
* Fix line-number and scroll bugs (#33094) #33095
|
||||
* Fix bleve fuzziness search (#33078) #33087
|
||||
* Fix broken forms #33082
|
||||
* Fix empty repo updated time (#33120) #33124
|
||||
* Add missing transaction when set merge #33113
|
||||
* Fix issue comment number (#30556) #33055
|
||||
* Fix duplicate co-author in squashed merge commit messages (#33020) #33054
|
||||
* Fix Agit pull request permission check (#32999) #33005
|
||||
* Fix scoped label ui when contains emoji (#33007) #33014
|
||||
* Fix bug on activities (#33008) #33016
|
||||
* Fix review code comment avatar alignment (#33031) #33032
|
||||
* Fix templating in pull request comparison (#33025) #33038
|
||||
* Fix bug automerge cannot be chosed when there is only 1 merge style (#33040) #33043
|
||||
* Fix settings not being loaded at CLI (#26402) #33048
|
||||
* Support for email addresses containing uppercase characters when activating user account (#32998) #33001
|
||||
* Support org labels when adding labels by label names (#32988) #32996
|
||||
* Do not render truncated links in markdown (#32980) #32983
|
||||
* Demilestone should not include milestone (#32923) #32979
|
||||
* Fix Azure blob object Seek (#32974) #32975
|
||||
* Fix maven pom inheritance (#32943) #32976
|
||||
* Fix textarea newline handle (#32966) #32977
|
||||
* Fix outdated tmpl code (#32953) #32961
|
||||
* Fix commit range paging (#32944) #32962
|
||||
* Fix repo avatar conflict (#32958) #32960
|
||||
* Fix trailing comma not matched in the case of alphanumeric issue (#32945)
|
||||
* Relax the version checking for Arch packages (#32908) #32913
|
||||
* Add more load functions to make sure the reference object loaded (#32901) #32912
|
||||
* Filter reviews of one pull request in memory instead of database to reduce slow response because of lacking database index (#33106) #33128
|
||||
* Fix git remote error check, fix dependencies, fix js error (#33129) #33133
|
||||
|
||||
* MISC
|
||||
* Optimize branch protection rule loading (#32280)
|
||||
* Bump to go 1.23 (#31855)
|
||||
* Remove unused call to $.HeadRepo in view_title template (#32317)
|
||||
* Do not display `attestation-manifest` and use short sha256 instead of full sha256 (#32851)
|
||||
* Upgrade htmx to 2.0.4 (#32834)
|
||||
* Improve JSX/TSX support in code editor (#32833)
|
||||
* Add User-Agent for gitea's self-implemented lfs client. (#32832)
|
||||
* Use errors.New to replace fmt.Errorf with no parameters (#32800)
|
||||
* Add "n commits" link to contributors in contributors graph page (#32799)
|
||||
* Update dependencies, tweak eslint (#32719)
|
||||
* Remove all "floated" CSS styles (#32691)
|
||||
* Show tag name on branch/tag selector if repo shown from tag ref (#32689)
|
||||
* Use new mail package instead of an unmintained one (#32682)
|
||||
* Optimize the styling of icon buttons within file-header-right (#32675)
|
||||
* Validate OAuth Redirect URIs (#32643)
|
||||
* Support optional/configurable IAMEndpoint for Minio Client (#32581) (#32581)
|
||||
* Make search box in issue sidebar dropdown list always show when scrolling (#32576)
|
||||
* Bump CI,Flake and Snap to Node 22 (#32487)
|
||||
* Update `github.com/meilisearch/meilisearch-go` (#32484)
|
||||
* Add `DEFAULT_MIRROR_REPO_UNITS` and `DEFAULT_TEMPLATE_REPO_UNITS` options (#32416)
|
||||
* Update go dependencies (#32389)
|
||||
* Update JS and PY dependencies (#32388)
|
||||
* Upgrade rollup to 4.24.0 (#32312)
|
||||
* Upgrade vue to 3.5.12 (#32311)
|
||||
* Improve the maintainblity of the reserved username list (#32229)
|
||||
* Upgrade htmx to 2.0.3 (#32192)
|
||||
* Count typescript files as frontend for labeling (#32088)
|
||||
* Only use Host header from reverse proxy (#32060)
|
||||
* Failed authentications are logged to level Warning (#32016)
|
||||
* Enhance USER_DISABLED_FEATURES to allow disabling change username or full name (#31959)
|
||||
* Distinguish official vs non-official reviews, add tool tips, and upgr… (#31924)
|
||||
* Update mermaid to v11 (#31913)
|
||||
* Bump relative-time-element to v4.4.3 (#31910)
|
||||
* Upgrade `htmx` to `2.0.2` (#31847)
|
||||
* Add warning message in merge instructions when `AutodetectManualMerge` was not enabled (#31805)
|
||||
* Add types to various low-level functions (#31781)
|
||||
* Update JS dependencies (#31766)
|
||||
* Remove unused code from models/repos/release.go (#31756)
|
||||
* Support delete user email in admin panel (#31690)
|
||||
* Add `username` to OIDC introspection response (#31688)
|
||||
* Use GetDisplayName() instead of DisplayName() to generate rss feeds (#31687)
|
||||
* Code editor theme enhancements (#31629)
|
||||
* Update JS dependencies (#31616)
|
||||
* Add types for js globals (#31586)
|
||||
* Add back esbuild-loader for .js files (#31585)
|
||||
* Don't show hidden labels when filling out an issue template (#31576)
|
||||
* Allow synchronizing user status from OAuth2 login providers (#31572)
|
||||
* Display app name in the registration email title (#31562)
|
||||
* Use stable version of fabric (#31526)
|
||||
* Support legacy _links LFS batch responses (#31513)
|
||||
* Fix JS error with disabled attachment and easymde (#31511)
|
||||
* Always use HTML attributes for avatar size (#31509)
|
||||
* Use nolyfill to remove some polyfills (#31468)
|
||||
* Disable issue/PR comment button given empty input (#31463)
|
||||
* Add simple JS init performance trace (#31459)
|
||||
* Bump htmx to 2.0.0 (#31413)
|
||||
* Update JS dependencies, remove `eslint-plugin-jquery` (#31402)
|
||||
* Split org Propfile README to a new tab `overview` (#31373)
|
||||
* Update nix flake and add gofumpt (#31320)
|
||||
* Code optimization (#31315)
|
||||
* Enable poetry non-package mode (#31282)
|
||||
* Optimize profile layout to enhance visual experience (#31278)
|
||||
* Update `golang.org/x/net` (#31260)
|
||||
* Bump `@github/relative-time-element` to v4.4.1 (#31232)
|
||||
* Remove unnecessary inline style for tab-size (#31224)
|
||||
* Update golangci-lint to v1.59.0 (#31221)
|
||||
* Update chroma to v2.14.0 (#31177)
|
||||
* Update JS dependencies (#31120)
|
||||
* Improve the handling of `jobs.<job_id>.if` (#31070)
|
||||
* Clean up revive linter config, tweak golangci output (#30980)
|
||||
* Use CSS `inset` shorthand (#30939)
|
||||
* Forbid deprecated `break-word` in CSS (#30934)
|
||||
* Remove obsolete monaco workaround (#30893)
|
||||
* Update JS dependencies, add new eslint rules (#30840)
|
||||
* Fix body margin shifting with modals, fix error on project column edit (#30831)
|
||||
* Remove disk-clean workflow (#30741)
|
||||
* Bump `github.com/google/go-github` to v61 (#30738)
|
||||
* Add built js files to eslint ignore (#30737)
|
||||
* Use `ProtonMail/go-crypto` for `opengpg` in tests (#30736)
|
||||
* Upgrade xorm to v1.3.9 and improve some migrations Sync (#29899)
|
||||
* Added default sorting milestones by name (#27084)
|
||||
* Enable `unparam` linter (#31277)
|
||||
* Use Alpine 3.21 for the docker images (#32924) #32951
|
||||
* Bump x/net (#32896) #32899
|
||||
* Use -s -w ldflags for release artifacts (#33041) #33042
|
||||
* Remove aws go sdk package dependency (#33029) #33047
|
||||
|
||||
## [1.22.6](https://github.com/go-gitea/gitea/releases/tag/v1.22.6) - 2024-12-12
|
||||
|
||||
* SECURITY
|
||||
* Fix misuse of PublicKeyCallback(#32810)
|
||||
* BUGFIXES
|
||||
* Fix lfs migration (#32812) (#32818)
|
||||
* Add missing two sync feed for refs/pull (#32815)
|
||||
* TESTING
|
||||
* Avoid MacOS keychain dialog in integration tests (#32813) (#32816)
|
||||
|
||||
## [1.22.5](https://github.com/go-gitea/gitea/releases/tag/v1.22.5) - 2024-12-11
|
||||
|
||||
* SECURITY
|
||||
* Upgrade crypto library (#32791)
|
||||
* Fix delete branch perm checking (#32654) (#32707)
|
||||
* BUGFIXES
|
||||
* Add standard-compliant route to serve outdated R packages (#32783) (#32789)
|
||||
* Fix internal server error when updating labels without write permission (#32776) (#32785)
|
||||
* Add Swift login endpoint (#32693) (#32701)
|
||||
* Fix fork page branch selection (#32711) (#32725)
|
||||
* Fix word overflow in file search page (#32695) (#32699)
|
||||
* Fix gogit `GetRefCommitID` (#32705) (#32712)
|
||||
* Fix race condition in mermaid observer (#32599) (#32673)
|
||||
* Fixe a keystring misuse and refactor duplicates keystrings (#32668) (#32792)
|
||||
* Bump relative-time-element to v4.4.4 (#32739)
|
||||
* PERFORMANCE
|
||||
* Make wiki pages visit fast (#32732) (#32745)
|
||||
* MISC
|
||||
* Don't create action when syncing mirror pull refs (#32659) (#32664)
|
||||
|
||||
## [1.22.4](https://github.com/go-gitea/gitea/releases/tag/v1.22.4) - 2024-11-14
|
||||
|
||||
* SECURITY
|
||||
* Fix basic auth with webauthn (#32531) (#32536)
|
||||
* Refactor internal routers (partial backport, auth token const time comparing) (#32473) (#32479)
|
||||
* PERFORMANCE
|
||||
* Remove transaction for archive download (#32186) (#32520)
|
||||
* BUGFIXES
|
||||
* Fix `missing signature key` error when pulling Docker images with `SERVE_DIRECT` enabled (#32365) (#32397)
|
||||
* Fix get reviewers fails when selecting user without pull request permissions unit (#32415) (#32616)
|
||||
* Fix adding index files to tmp directory (#32360) (#32593)
|
||||
* Fix PR creation on forked repositories via API (#31863) (#32591)
|
||||
* Fix missing menu tabs in organization project view page (#32313) (#32592)
|
||||
* Support HTTP POST requests to `/userinfo`, aligning to OpenID Core specification (#32578) (#32594)
|
||||
* Fix debian package clean up cron job (#32351) (#32590)
|
||||
* Fix GetInactiveUsers (#32540) (#32588)
|
||||
* Allow the actions user to login via the jwt token (#32527) (#32580)
|
||||
* Fix submodule parsing (#32571) (#32577)
|
||||
* Refactor find forks and fix possible bugs that weaken permissions check (#32528) (#32547)
|
||||
* Fix some places that don't respect org full name setting (#32243) (#32550)
|
||||
* Refactor push mirror find and add check for updating push mirror (#32539) (#32549)
|
||||
* Fix basic auth with webauthn (#32531) (#32536)
|
||||
* Fix artifact v4 upload above 8MB (#31664) (#32523)
|
||||
* Fix oauth2 error handle not return immediately (#32514) (#32516)
|
||||
* Fix action not triggered when commit message is too long (#32498) (#32507)
|
||||
* Fix `GetRepoLink` nil pointer dereference on dashboard feed page when repo is deleted with actions enabled (#32501) (#32502)
|
||||
* Fix `missing signature key` error when pulling Docker images with `SERVE_DIRECT` enabled (#32397) (#32397)
|
||||
* Fix the permission check for user search API and limit the number of returned users for `/user/search` (#32310)
|
||||
* Fix SearchIssues swagger docs (#32208) (#32298)
|
||||
* Fix dropdown content overflow (#31610) (#32250)
|
||||
* Disable Oauth check if oauth disabled (#32368) (#32480)
|
||||
* Respect renamed dependencies of Cargo registry (#32430) (#32478)
|
||||
* Fix mermaid diagram height when initially hidden (#32457) (#32464)
|
||||
* Fix broken releases when re-pushing tags (#32435) (#32449)
|
||||
* Only provide the commit summary for Discord webhook push events (#32432) (#32447)
|
||||
* Only query team tables if repository is under org when getting assignees (#32414) (#32426)
|
||||
* Fix created_unix for mirroring (#32342) (#32406)
|
||||
* Respect UI.ExploreDefaultSort setting again (#32357) (#32385)
|
||||
* Fix broken image when editing comment with non-image attachments (#32319) (#32345)
|
||||
* Fix disable 2fa bug (#32320) (#32330)
|
||||
* Always update expiration time when creating an artifact (#32281) (#32285)
|
||||
* Fix null errors on conversation holder (#32258) (#32266) (#32282)
|
||||
* Only rename a user when they should receive a different name (#32247) (#32249)
|
||||
* Fix checkbox bug on private/archive filter (#32236) (#32240)
|
||||
* Add a doctor check to disable the "Actions" unit for mirrors (#32424) (#32497)
|
||||
* Quick fix milestone deadline 9999 (#32423)
|
||||
* Make `show stats` work when only one file changed (#32244) (#32268)
|
||||
* Make `owner/repo/pulls` handlers use "PR reader" permission (#32254) (#32265)
|
||||
* Update scheduled tasks even if changes are pushed by "ActionsUser" (#32246) (#32252)
|
||||
* MISC
|
||||
* Remove unnecessary code: `GetPushMirrorsByRepoID` called on all repo pages (#32560) (#32567)
|
||||
* Improve some sanitizer rules (#32534)
|
||||
* Update nix development environment vor v1.22.x (#32495)
|
||||
* Add warn log when deleting inactive users (#32318) (#32321)
|
||||
* Update github.com/go-enry/go-enry to v2.9.1 (#32295) (#32296)
|
||||
* Warn users when they try to use a non-root-url to sign in/up (#32272) (#32273)
|
||||
|
||||
## [1.22.3](https://github.com/go-gitea/gitea/releases/tag/v1.22.3) - 2024-10-08
|
||||
|
||||
* SECURITY
|
||||
* Fix bug when a token is given public only (#32204) (#32218)
|
||||
* PERFORMANCE
|
||||
* Increase `cacheContextLifetime` to reduce false reports (#32011) (#32023)
|
||||
* Don't join repository when loading action table data (#32127) (#32143)
|
||||
* BUGFIXES
|
||||
* Fix javascript error when an anonymous user visits migration page (#32144) (#32179)
|
||||
* Don't init signing keys if oauth2 provider is disabled (#32177)
|
||||
* Fix wrong status of `Set up Job` when first step is skipped (#32120) (#32125)
|
||||
* Fix bug when deleting a migrated branch (#32075) (#32123)
|
||||
* Truncate commit message during Discord webhook push events (#31970) (#32121)
|
||||
* Allow to set branch protection in an empty repository (#32095) (#32119)
|
||||
* Fix panic when cloning with wrong ssh format. (#32076) (#32118)
|
||||
* Fix rename branch permission bug (#32066) (#32108)
|
||||
* Fix: database not update release when using `git push --tags --force` (#32040) (#32074)
|
||||
* Add missing comment reply handling (#32050) (#32065)
|
||||
* Do not escape relative path in RPM primary index (#32038) (#32054)
|
||||
* Fix `/repos/{owner}/{repo}/pulls/{index}/files` endpoint not populating `previous_filename` (#32017) (#32028)
|
||||
* Support allowed hosts for migrations to work with proxy (#32025) (#32026)
|
||||
* Fix the logic of finding the latest pull review commit ID (#32139) (#32165)
|
||||
* Fix bug in getting merged pull request by commit (#32079) (#32117)
|
||||
* Fix wrong last modify time (#32102) (#32104)
|
||||
* Fix incorrect `/tokens` api (#32085) (#32092)
|
||||
* Handle invalid target when creating releases using API (#31841) (#32043)
|
||||
* Check if the `due_date` is nil when editing issues (#32035) (#32042)
|
||||
* Fix container parallel upload bugs (#32022)
|
||||
* Fixed race condition when deleting documents by repoId in ElasticSearch (#32185) (#32188)
|
||||
* Refactor CSRF protector (#32057) (#32069)
|
||||
* Fix Bug in Issue/pulls list (#32081) (#32115)
|
||||
* Include collaboration repositories on dashboard source/forks/mirrors list (#31946) (#32122)
|
||||
* Add null check for responseData.invalidTopics (#32212) (#32217)
|
||||
* TESTING
|
||||
* Fix mssql ci with a new mssql version on ci (#32094)
|
||||
* MISC
|
||||
* Upgrade some dependencies include minio-go (#32166)
|
||||
* Add bin to Composer Metadata (#32099) (#32106)
|
||||
* Lazy load avatar images (#32051) (#32063)
|
||||
* Upgrade cache to v0.2.1 (#32003) (#32009)
|
||||
|
||||
## [1.22.2](https://github.com/go-gitea/gitea/releases/tag/v1.22.2) - 2024-08-28
|
||||
|
||||
* Security
|
||||
* Replace v-html with v-text in search inputbox (#31966) (#31973)
|
||||
* Fix nuget/conan/container packages upload bugs (#31967) (#31982)
|
||||
* PERFORMANCE
|
||||
* Refactor the usage of batch catfile (#31754) (#31889)
|
||||
* BUGFIXES
|
||||
* Fix overflowing content in action run log (#31842) (#31853)
|
||||
* Scroll images in project issues separately from the remaining issue (#31683) (#31823)
|
||||
* Add `:focus-visible` style to buttons (#31799) (#31819)
|
||||
* Fix the display of project type for deleted projects (#31732) (#31734)
|
||||
* Fix API owner ID should be zero when created repo secret (#31715) (#31811)
|
||||
* Set owner id to zero when GetRegistrationToken for repo (#31725) (#31729)
|
||||
* Fix API endpoint for registration-token (#31722) (#31728)
|
||||
* Add permission check when creating PR (#31033) (#31720)
|
||||
* Don't return 500 if mirror url contains special chars (#31859) (#31895)
|
||||
* Fix agit automerge (#31207) (#31881)
|
||||
* Add CfTurnstileSitekey context data to all captcha templates (#31874) (#31876)
|
||||
* Avoid returning without written ctx when posting PR (#31843) (#31848)
|
||||
* Fix raw wiki links (#31825) (#31845)
|
||||
* Fix panic of ssh public key page after deletion of auth source (#31829) (#31836)
|
||||
* Fixes for unreachable project issues when transfer repository from organization (#31770) (#31828)
|
||||
* Show lock owner instead of repo owner on LFS setting page (#31788) (#31817)
|
||||
* Fix `IsObjectExist` with gogit (#31790) (#31806)
|
||||
* Fix protected branch files detection on pre_receive hook (#31778) (#31796)
|
||||
* Add `TAGS` to `TEST_TAGS` and fix bugs found with gogit (#31791) (#31795)
|
||||
* Rename head branch of pull requests when renaming a branch (#31759) (#31774)
|
||||
* Fix wiki revision pagination (#31760) (#31772)
|
||||
* Bump vue-bar-graph (#31705) (#31753)
|
||||
* Distinguish LFS object errors to ignore missing objects during migration (#31702) (#31745)
|
||||
* Make GetRepositoryByName more safer (#31712) (#31718)
|
||||
* Fix a branch divergence cache bug (#31659) (#31661)
|
||||
* Allow org team names of length 255 in create team form (#31564) (#31603)
|
||||
* Use old behavior for telegram webhook (#31588)
|
||||
* Bug fix for translation in ru (#31892)
|
||||
* Fix actions notify bug (#31866) (#31875)
|
||||
* Fix the component of access token list not mounted (#31824) (#31868)
|
||||
* Add missing repository type filter parameters to pager (#31832) (#31837)
|
||||
* Fix dates displaying in a wrong manner when we're close to the end of… (#31750)
|
||||
* Fix "Filter by commit" Dropdown (#31695) (#31696)
|
||||
* Properly filter issue list given no assignees filter (#31522) (#31685)
|
||||
* Prevent update pull refs manually and will not affect other refs update (#31931)(#31955)
|
||||
* Fix sort order for organization home and user profile page (#31921) (#31922)
|
||||
* Fix search team (#31923) (#31942)
|
||||
* Fix 500 error when state params is set when editing issue/PR by API (#31880) (#31952)
|
||||
* Fix index too many file names bug (#31903) (#31953)
|
||||
* Add lock for parallel maven upload (#31851) (#31954)
|
||||
* MISC
|
||||
* Remove "dsa-1024" testcases from Test_SSHParsePublicKey and Test_calcFingerprint (#31905) (#31914)
|
||||
* Upgrade bleve to 2.4.2 (#31894)
|
||||
* Remove unneccessary uses of `word-break: break-all` (#31637) (#31652)
|
||||
* Return an empty string when a repo has no avatar in the repo API (#31187) (#31567)
|
||||
* Upgrade micromatch to 4.0.8 (#31944)
|
||||
* Update webpack to 5.94.0 (#31941)
|
||||
|
||||
## [1.22.1](https://github.com/go-gitea/gitea/releases/tag/v1.22.1) - 2024-07-04
|
||||
|
||||
* SECURITY
|
||||
* Add replacement module for `mholt/archiver` (#31267) (#31270)
|
||||
* API
|
||||
* Fix missing images in editor preview due to wrong links (#31299) (#31393)
|
||||
* Fix duplicate sub-path for avatars (#31365) (#31368)
|
||||
* Reduce memory usage for chunked artifact uploads to MinIO (#31325) (#31338)
|
||||
* Remove sub-path from container registry realm (#31293) (#31300)
|
||||
* Fix NuGet Package API for $filter with Id equality (#31188) (#31242)
|
||||
* Add an immutable tarball link to archive download headers for Nix (#31139) (#31145)
|
||||
* Add missed return after `ctx.ServerError` (#31130) (#31133)
|
||||
* BUGFIXES
|
||||
* Fix avatar radius problem on the new issue page (#31506) (#31508)
|
||||
* Fix overflow menu flickering on mobile (#31484) (#31488)
|
||||
* Fix poor table column width due to breaking words (#31473) (#31477)
|
||||
* Support relative paths to videos from Wiki pages (#31061) (#31453)
|
||||
* Fix new issue/pr avatar (#31419) (#31424)
|
||||
* Increase max length of org team names from 30 to 255 characters (#31410) (#31421)
|
||||
* Fix line number width in code preview (#31307) (#31316)
|
||||
* Optimize runner-tags layout to enhance visual experience (#31258) (#31263)
|
||||
* Fix overflow on push notification (#31179) (#31238)
|
||||
* Fix overflow on notifications (#31178) (#31237)
|
||||
* Fix overflow in issue card (#31203) (#31225)
|
||||
* Split sanitizer functions and fine-tune some tests (#31192) (#31200)
|
||||
* use correct l10n string (#31487) (#31490)
|
||||
* Fix dropzone JS error when attachment is disabled (#31486)
|
||||
* Fix web notification icon not updated once you read all notifications (#31447) (#31466)
|
||||
* Switch to "Write" tab when edit comment again (#31445) (#31461)
|
||||
* Fix the link for .git-blame-ignore-revs bypass (#31432) (#31442)
|
||||
* Fix the wrong line number in the diff view page when expanded twice. (#31431) (#31440)
|
||||
* Fix labels and projects menu overflow on issue page (#31435) (#31439)
|
||||
* Fix Account Linking UpdateMigrationsByType (#31428) (#31434)
|
||||
* Fix markdown math brackets render problem (#31420) (#31430)
|
||||
* Fix rendered wiki page link (#31398) (#31407)
|
||||
* Fix natural sort (#31384) (#31394)
|
||||
* Allow downloading attachments of draft releases (#31369) (#31380)
|
||||
* Fix repo graph JS (#31377)
|
||||
* Fix incorrect localization `explorer.go` (#31348) (#31350)
|
||||
* Fix hash render end with colon (#31319) (#31346)
|
||||
* Fix line number widths (#31341) (#31343)
|
||||
* Fix navbar `+` menu flashing on page load (#31281) (#31342)
|
||||
* Fix adopt repository has empty object name in database (#31333) (#31335)
|
||||
* Delete legacy cookie before setting new cookie (#31306) (#31317)
|
||||
* Fix some URLs whose sub-path is missing (#31289) (#31292)
|
||||
* Fix admin oauth2 custom URL settings (#31246) (#31247)
|
||||
* Make pasted "img" tag has the same behavior as markdown image (#31235) (#31243)
|
||||
* Fix agit checkout command line hint & fix ShowMergeInstructions checking (#31219) (#31222)
|
||||
* Fix the possible migration failure on 286 with postgres 16 (#31209) (#31218)
|
||||
* Fix branch order (#31174) (#31193)
|
||||
* Fix markup preview (#31158) (#31166)
|
||||
* Fix push multiple branches error with tests (#31151) (#31153)
|
||||
* Fix API repository object format missed (#31118) (#31132)
|
||||
* Fix missing memcache import (#31105) (#31109)
|
||||
* Upgrade `github.com/hashicorp/go-retryablehttp` (#31499)
|
||||
* Fix double border in system status table (#31363) (#31401)
|
||||
* Fix bug filtering issues which have no project (#31337) (#31367)
|
||||
* Fix #31185 try fix lfs download from bitbucket failed (#31201) (#31329)
|
||||
* Add nix flake for dev shell (#30967) (#31310)
|
||||
* Fix and clean up `ConfirmModal` (#31283) (#31291)
|
||||
* Optimize repo-list layout to enhance visual experience (#31272) (#31276)
|
||||
* fixed the dropdown menu for the top New button to expand to the left (#31273) (#31275)
|
||||
* Fix Activity Page Contributors dropdown (#31264) (#31269)
|
||||
* fix: allow actions artifacts storage migration to complete succesfully (#31251) (#31257)
|
||||
* Make blockquote attention recognize more syntaxes (#31240) (#31250)
|
||||
* Remove .segment from .project-column (#31204) (#31239)
|
||||
* Ignore FindRecentlyPushedNewBranches err (#31164) (#31171)
|
||||
* Use vertical layout for multiple code expander buttons (#31122) (#31152)
|
||||
* Remove duplicate `ProxyPreserveHost` in Apache httpd doc (#31143) (#31147)
|
||||
* Improve mobile review ui (#31091) (#31136)
|
||||
* Fix DashboardRepoList margin (#31121) (#31128)
|
||||
* Update pip related commands for docker (#31106) (#31111)
|
||||
|
||||
## [1.22.0](https://github.com/go-gitea/gitea/releases/tag/v1.22.0) - 2024-05-27
|
||||
|
||||
This release stands as a monumental milestone in our development journey with a record-breaking incorporation of [1528](https://github.com/go-gitea/gitea/pulls?q=is%3Apr+milestone%3A1.22.0+is%3Amerged) pull requests. It marks the most extensive update in Gitea's history, showcasing a plethora of new features and infrastructure improvements.
|
||||
|
||||
Noteworthy advancements in this release include the introduction of `HTMX` and `Tailwind`, signaling a strategic shift as we gradually phase out `jquery` and `Fomantic UI`. These changes reflect our commitment to embracing modern technologies and enhancing the user experience.
|
||||
|
||||
Key highlights of this release encompass significant changes categorized under `BREAKING`, `FEATURES`, `ENHANCEMENTS`, and `PERFORMANCE`, each contributing to a more robust and efficient Gitea platform.
|
||||
|
||||
* BREAKING
|
||||
* Improve reverse proxy documents and clarify the AppURL guessing behavior (#31003) (#31020)
|
||||
* Remember log in for a month by default (#30150)
|
||||
* Breaking summary for template refactoring (#29395)
|
||||
* All custom templates need to follow these changes
|
||||
* Recommend/convert to use case-sensitive collation for MySQL/MSSQL (#28662)
|
||||
* Make offline mode as default to not connect external avatar service by default (#28548)
|
||||
* Include public repos in the doer's dashboard for issue search (#28304)
|
||||
* Use restricted sanitizer for repository description (#28141)
|
||||
* Support storage base path as prefix (#27827)
|
||||
* Enhanced auth token / remember me (#27606)
|
||||
* Rename the default themes to `gitea-light`, `gitea-dark`, `gitea-auto` (#27419)
|
||||
* If you didn't see the new themes, please remove the `[ui].THEMES` config option from `app.ini`
|
||||
* Require MySQL 8.0, PostgreSQL 12, MSSQL 2012 (#27337)
|
||||
* FEATURES
|
||||
* Allow everyone to read or write a wiki by a repo unit setting (#30495)
|
||||
* Use raw Wiki links for non-renderable Wiki files (#30273)
|
||||
* Render embedded code preview by permalink in markdown (#30234) (#30249)
|
||||
* Support repo code search without setting up an indexer (#29998)
|
||||
* Support pasting URLs over markdown text (#29566)
|
||||
* Allow to change primary email before account activation (#29412)
|
||||
* Customizable "Open with" applications for repository clone (#29320)
|
||||
* Allow options to disable user deletion from the interface on app.ini (#29275)
|
||||
* Extend issue template YAML engine (#29274)
|
||||
* Add support for `linguist-detectable` and `linguist-documentation` (#29267)
|
||||
* Implement code frequency graph (#29191)
|
||||
* Show commit status for releases (#29149)
|
||||
* Add user blocking (#29028)
|
||||
* Actions Artifacts v4 backend (#28965)
|
||||
* Add merge style `fast-forward-only` (#28954)
|
||||
* Retarget depending pulls when the parent branch is deleted (#28686)
|
||||
* Add global setting on how timestamps should be rendered (#28657)
|
||||
* Implement actions badge SVGs (#28102)
|
||||
* Add skip ci functionality (#28075)
|
||||
* Show latest commit for file (#28067)
|
||||
* Allow to sync tags from the admin dashboard (#28045)
|
||||
* Add Profile Readme for Organisations (#27955)
|
||||
* Implement contributors graph (#27882)
|
||||
* Artifact deletion in actions ui (#27172)
|
||||
* Add API routes to get runner registration token (#27144)
|
||||
* Add support for forking single branch (#25821)
|
||||
* Add support for sha256 repositories (#23894)
|
||||
* Add admin API route for managing user's badges (#23106)
|
||||
* ENHANCEMENTS
|
||||
* Make gitea webhooks openproject compatible (#28435) (#31081)
|
||||
* Support using label names when changing issue labels (#30943) (#30958)
|
||||
* Fix various problems around project board view (#30696) (#30902)
|
||||
* Improve context popup rendering (#30824) (#30829)
|
||||
* Allow to save empty comment (#30706)
|
||||
* Prevent allow/reject reviews on merged/closed PRs (#30686)
|
||||
* Initial support for colorblindness-friendly themes (#30625)
|
||||
* Some NuGet package enhancements (#30280) (#30324)
|
||||
* Markup color and font size fixes (#30282) (#30310)
|
||||
* Show 12 lines in markup code preview (#30255) (#30257)
|
||||
* Add `[other].SHOW_FOOTER_POWERED_BY` setting to hide `Powered by` (#30253)
|
||||
* Pulse page improvements (#30149)
|
||||
* Render code tags in commit messages (#30146)
|
||||
* Prevent re-review and dismiss review actions on closed and merged PRs (#30065)
|
||||
* Cancel previous runs of the same PR automatically (#29961)
|
||||
* Drag-and-drop improvements for projects and issue pins (#29875)
|
||||
* Add default board to new projects, remove uncategorized pseudo-board (#29874)
|
||||
* Prevent layout shift in `<overflow-menu>` items (#29831)
|
||||
* Add skip ci support for pull request title (#29774)
|
||||
* Add more stats tables (#29730)
|
||||
* Update API to return 'source_id' for users (#29718)
|
||||
* Determine fuzziness of bleve indexer by keyword length (#29706)
|
||||
* Expose fuzzy search for issues/pulls (#29701)
|
||||
* Put an edit file button on pull request files to allow a quick operation (#29697)
|
||||
* Fix action runner offline label padding (#29691)
|
||||
* Update allowed attachment types (#29688)
|
||||
* Completely style the webkit autofill (#29683)
|
||||
* Highlight archived labels (#29680)
|
||||
* Add a warning for disallowed email domains (#29658)
|
||||
* Set user's 24h preference from their current OS locale (#29651)
|
||||
* Add setting to disable user features when user login type is not plain (#29615)
|
||||
* Improve natural sort (#29611)
|
||||
* Make wiki default branch name changeable (#29603)
|
||||
* Unify search boxes (#29530)
|
||||
* Add support for API blob upload of release attachments (#29507)
|
||||
* Detect broken git hooks (#29494)
|
||||
* Sync branches to DB immediately when handling git hook calling (#29493)
|
||||
* Allow options to disable user GPG key configuration from the interface on app.ini (#29486)
|
||||
* Allow options to disable user SSH key configuration from the interface on app.ini (#29447)
|
||||
* Use relative links for commits, mentions, and issues in markdown (#29427)
|
||||
* Add `<overflow-menu>`, rename webcomponents (#29400)
|
||||
* Include resource state events in Gitlab downloads (#29382)
|
||||
* Properly migrate target branch change GitLab comment (#29340)
|
||||
* Recolor dark theme to blue shade (#29283)
|
||||
* Partially enable MSSQL case-sensitive collation support (#29238)
|
||||
* Auto-update the system status in the admin dashboard (#29163)
|
||||
* Integrate alpine `noarch` packages into other architectures index (#29137)
|
||||
* Document how the TOC election process works (#29135)
|
||||
* Tweak repo header (#29134)
|
||||
* Make blockquote border size less aggressive (#29124)
|
||||
* Downscale pasted PNG images based on metadata (#29123)
|
||||
* Show `View at this point in history` for every commit (#29122)
|
||||
* Add support for action artifact serve direct (#29120)
|
||||
* Change webhook-type in create-view (#29114)
|
||||
* Drop "@" from the email sender to avoid spam filters (#29109)
|
||||
* Allow non-admin users to delete review requests (#29057)
|
||||
* Improve user search display name (#29002)
|
||||
* Include username in email headers (#28981)
|
||||
* Show whether a PR is WIP inside popups (#28975)
|
||||
* Also match weakly validated ETags (#28957)
|
||||
* Support nuspec manifest download for Nuget packages (#28921)
|
||||
* Fix hardcoded GitHub icon used as migrated release avatar (#28910)
|
||||
* Propagate install_if and provider_priority to APKINDEX (#28899)
|
||||
* Add artifacts v4 JWT to job message and accept it (#28885)
|
||||
* Enable/disable owner and repo projects independently (#28805)
|
||||
* Add non-JS fallback for reaction tooltips (#28785)
|
||||
* Add the ability to see open and closed issues at the same time (#28757)
|
||||
* Move sign-in labels to be above inputs (#28753)
|
||||
* Display the latest sync time for pull mirrors on the repo page (#28712)
|
||||
* Show in Web UI if the file is vendored and generated (#28620)
|
||||
* Add orphaned topic consistency check (#28507)
|
||||
* Add branch protection setting for ignoring stale approvals (#28498)
|
||||
* Add option to set language in admin user view (#28449)
|
||||
* Fix incorrect run order of action jobs (#28367)
|
||||
* Add missing exclusive in advanced label options (#28322)
|
||||
* Added instance-level variables (#28115)
|
||||
* Add edit option for README.md (#28071)
|
||||
* Fix link to `Code` tab on wiki commits (#28041)
|
||||
* Allow to set explore page default sort (#27951)
|
||||
* Improve PR diff view on mobile (#27883)
|
||||
* Properly migrate automatic merge GitLab comments (#27873)
|
||||
* Display issue task list on project cards (#27865)
|
||||
* Add Index to pull_auto_merge.doer_id (#27811)
|
||||
* Fix display member unit in the menu bar if there are no hidden members in public org (#27795)
|
||||
* List all Debian package versions in `Packages` (#27786)
|
||||
* Allow pull requests Manually Merged option to be used by non-admins (#27780)
|
||||
* Only show diff file tree when more than one file changed (#27775)
|
||||
* Show placeholder email in privacy popup (#27770)
|
||||
* Revamp repo header (#27760)
|
||||
* Add `must-change-password` command line parameter (#27626)
|
||||
* Unify password changing and invalidate auth tokens (#27625)
|
||||
* Add border to file tree 'sub-items' and add padding to 'item-file' (#27593)
|
||||
* Add slow SQL query warning (#27545)
|
||||
* Pre-register OAuth application for tea (#27509)
|
||||
* Differentiate between `push` and `pull` `mirror sync in progress` (#27390)
|
||||
* Link to file from its history (#27354)
|
||||
* Add a shortcut to user's profile page to admin user details (#27299)
|
||||
* Doctor: delete action entries without existing user (#27292)
|
||||
* Show total TrackedTime on issue/pull/milestone lists (#26672)
|
||||
* Don't show the new pull request button when the page is not compare pull (#26431)
|
||||
* Add `Hide/Show all checks` button to commit status check (#26284)
|
||||
* Improvements of releases list and tags list (#25859)
|
||||
* PERFORMANCE
|
||||
* Fix package list performance (#30520) (#30616)
|
||||
* Add commit status summary table to reduce query from commit status table (#30223)
|
||||
* Refactor markup/csv: don't read all to memory (#29760)
|
||||
* Lazy load object format with command line and don't do it in OpenRepository (#29712)
|
||||
* Add cache for branch divergence on branch list page (#29577)
|
||||
* Do some performance optimization for issues list and view issue/pull (#29515)
|
||||
* Cache repository default branch commit status to reduce query on commit status table (#29444)
|
||||
* Use `crypto/sha256` (#29386)
|
||||
* Some performance optimization on the dashboard and issues page (#29010)
|
||||
* Add combined index for issue_user.uid and issue_id (#28080)
|
||||
|
||||
## [1.21.11](https://github.com/go-gitea/gitea/releases/tag/v1.21.11) - 2024-04-07
|
||||
|
||||
* SECURITY
|
||||
|
|
|
@ -182,7 +182,7 @@ Here's how to run the test suite:
|
|||
|
||||
## Translation
|
||||
|
||||
All translation work happens on [Crowdin](https://crowdin.com/project/gitea).
|
||||
All translation work happens on [Crowdin](https://translate.gitea.com).
|
||||
The only translation that is maintained in this repository is [the English translation](https://github.com/go-gitea/gitea/blob/main/options/locale/locale_en-US.ini).
|
||||
It is synced regularly with Crowdin. \
|
||||
Other locales on main branch **should not** be updated manually as they will be overwritten with each sync. \
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Build stage
|
||||
FROM docker.io/library/golang:1.23-alpine3.20 AS build-env
|
||||
FROM docker.io/library/golang:1.24-alpine3.21 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY=${GOPROXY:-direct}
|
||||
|
@ -41,7 +41,7 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \
|
|||
/go/src/code.gitea.io/gitea/environment-to-ini
|
||||
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
|
||||
|
||||
FROM docker.io/library/alpine:3.20
|
||||
FROM docker.io/library/alpine:3.21
|
||||
LABEL maintainer="maintainers@gitea.io"
|
||||
|
||||
EXPOSE 22 3000
|
||||
|
@ -78,7 +78,7 @@ ENV GITEA_CUSTOM=/data/gitea
|
|||
VOLUME ["/data"]
|
||||
|
||||
ENTRYPOINT ["/usr/bin/entrypoint"]
|
||||
CMD ["/bin/s6-svscan", "/etc/s6"]
|
||||
CMD ["/usr/bin/s6-svscan", "/etc/s6"]
|
||||
|
||||
COPY --from=build-env /tmp/local /
|
||||
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Build stage
|
||||
FROM docker.io/library/golang:1.23-alpine3.20 AS build-env
|
||||
FROM docker.io/library/golang:1.24-alpine3.21 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY=${GOPROXY:-direct}
|
||||
|
@ -39,7 +39,7 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
|
|||
/go/src/code.gitea.io/gitea/environment-to-ini
|
||||
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
|
||||
|
||||
FROM docker.io/library/alpine:3.20
|
||||
FROM docker.io/library/alpine:3.21
|
||||
LABEL maintainer="maintainers@gitea.io"
|
||||
|
||||
EXPOSE 2222 3000
|
||||
|
|
|
@ -31,7 +31,6 @@ Gary Kim <gary@garykim.dev> (@gary-kim)
|
|||
Guillermo Prandi <gitea.maint@mailfilter.com.ar> (@guillep2k)
|
||||
Mura Li <typeless@ctli.io> (@typeless)
|
||||
6543 <6543@obermui.de> (@6543)
|
||||
jaqra <jaqra@hotmail.com> (@jaqra)
|
||||
David Svantesson <davidsvantesson@gmail.com> (@davidsvantesson)
|
||||
a1012112796 <1012112796@qq.com> (@a1012112796)
|
||||
Karl Heinz Marbaise <kama@soebes.de> (@khmarbaise)
|
||||
|
@ -46,7 +45,6 @@ Wim <wim@42.be> (@42wim)
|
|||
Jason Song <i@wolfogre.com> (@wolfogre)
|
||||
Yarden Shoham <git@yardenshoham.com> (@yardenshoham)
|
||||
Yu Tian <zettat123@gmail.com> (@Zettat123)
|
||||
Eddie Yang <576951401@qq.com> (@yp05327)
|
||||
Dong Ge <gedong_1994@163.com> (@sillyguodong)
|
||||
Xinyi Gong <hestergong@gmail.com> (@HesterG)
|
||||
wxiaoguang <wxiaoguang@gmail.com> (@wxiaoguang)
|
||||
|
@ -63,3 +61,6 @@ Tim-Niclas Oelschläger <zokki.softwareschmiede@gmail.com> (@zokkis)
|
|||
Yu Liu <1240335630@qq.com> (@HEREYUA)
|
||||
Kemal Zebari <kemalzebra@gmail.com> (@kemzeb)
|
||||
Rowan Bohde <rowan.bohde@gmail.com> (@bohde)
|
||||
hiifong <i@hiif.ong> (@hiifong)
|
||||
metiftikci <metiftikci@hotmail.com> (@metiftikci)
|
||||
Christopher Homberger <christopher.homberger@web.de> (@ChristopherHX)
|
||||
|
|
263
Makefile
263
Makefile
|
@ -23,20 +23,20 @@ SHASUM ?= shasum -a 256
|
|||
HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes)
|
||||
COMMA := ,
|
||||
|
||||
XGO_VERSION := go-1.23.x
|
||||
XGO_VERSION := go-1.24.x
|
||||
|
||||
AIR_PACKAGE ?= github.com/air-verse/air@v1
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.1.2
|
||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.60.3
|
||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
|
||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.5.1
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.5
|
||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.12
|
||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.6.0
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0
|
||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
|
||||
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1
|
||||
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1
|
||||
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.15.3
|
||||
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.17.1
|
||||
|
||||
DOCKER_IMAGE ?= gitea/gitea
|
||||
DOCKER_TAG ?= latest
|
||||
|
@ -73,6 +73,7 @@ EXTRA_GOFLAGS ?=
|
|||
MAKE_VERSION := $(shell "$(MAKE)" -v | cat | head -n 1)
|
||||
MAKE_EVIDENCE_DIR := .make_evidence
|
||||
|
||||
GOTESTFLAGS ?=
|
||||
ifeq ($(RACE_ENABLED),true)
|
||||
GOFLAGS += -race
|
||||
GOTESTFLAGS += -race
|
||||
|
@ -114,8 +115,6 @@ LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64
|
|||
GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/))
|
||||
MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list code.gitea.io/gitea/models/migrations/...)
|
||||
|
||||
FOMANTIC_WORK_DIR := web_src/fomantic
|
||||
|
||||
WEBPACK_SOURCES := $(shell find web_src/js web_src/css -type f)
|
||||
WEBPACK_CONFIGS := webpack.config.js tailwind.config.js
|
||||
WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css
|
||||
|
@ -139,14 +138,14 @@ TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
|
|||
|
||||
TEST_TAGS ?= $(TAGS_SPLIT) 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_LICENSE_TMP_DIR)
|
||||
TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR) $(GO_LICENSE_TMP_DIR)
|
||||
|
||||
GO_DIRS := build cmd models modules routers services tests
|
||||
WEB_DIRS := web_src/js web_src/css
|
||||
|
||||
ESLINT_FILES := web_src/js tools *.js *.ts tests/e2e
|
||||
ESLINT_FILES := web_src/js tools *.js *.ts *.cjs tests/e2e
|
||||
STYLELINT_FILES := web_src/css web_src/js/components/*.vue
|
||||
SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.js *.md *.yml *.yaml *.toml))
|
||||
SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.js *.md *.yml *.yaml *.toml)) $(filter-out tools/misspellings.csv, $(wildcard tools/*))
|
||||
EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini
|
||||
|
||||
GO_SOURCES := $(wildcard *.go)
|
||||
|
@ -165,10 +164,8 @@ ifdef DEPS_PLAYWRIGHT
|
|||
endif
|
||||
|
||||
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
|
||||
SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape}}/api/v1"|g
|
||||
SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape}}/api/v1"|"basePath": "/api/v1"|g
|
||||
SWAGGER_SPEC_INPUT := templates/swagger/v1_input.json
|
||||
SWAGGER_EXCLUDE := code.gitea.io/sdk
|
||||
SWAGGER_NEWLINE_COMMAND := -e '$$a\'
|
||||
|
||||
TEST_MYSQL_HOST ?= mysql:3306
|
||||
TEST_MYSQL_DBNAME ?= testgitea
|
||||
|
@ -189,67 +186,11 @@ TEST_MSSQL_PASSWORD ?= MwantsaSecurePassword1
|
|||
all: build
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo "Make Routines:"
|
||||
@echo " - \"\" equivalent to \"build\""
|
||||
@echo " - build build everything"
|
||||
@echo " - frontend build frontend files"
|
||||
@echo " - backend build backend files"
|
||||
@echo " - watch watch everything and continuously rebuild"
|
||||
@echo " - watch-frontend watch frontend files and continuously rebuild"
|
||||
@echo " - watch-backend watch backend files and continuously rebuild"
|
||||
@echo " - clean delete backend and integration files"
|
||||
@echo " - clean-all delete backend, frontend and integration files"
|
||||
@echo " - deps install dependencies"
|
||||
@echo " - deps-frontend install frontend dependencies"
|
||||
@echo " - deps-backend install backend dependencies"
|
||||
@echo " - deps-tools install tool dependencies"
|
||||
@echo " - deps-py install python dependencies"
|
||||
@echo " - lint lint everything"
|
||||
@echo " - lint-fix lint everything and fix issues"
|
||||
@echo " - lint-actions lint action workflow files"
|
||||
@echo " - lint-frontend lint frontend files"
|
||||
@echo " - lint-frontend-fix lint frontend files and fix issues"
|
||||
@echo " - lint-backend lint backend files"
|
||||
@echo " - lint-backend-fix lint backend files and fix issues"
|
||||
@echo " - lint-go lint go files"
|
||||
@echo " - lint-go-fix lint go files and fix issues"
|
||||
@echo " - lint-go-vet lint go files with vet"
|
||||
@echo " - lint-go-gopls lint go files with gopls"
|
||||
@echo " - lint-js lint js files"
|
||||
@echo " - lint-js-fix lint js files and fix issues"
|
||||
@echo " - lint-css lint css files"
|
||||
@echo " - lint-css-fix lint css files and fix issues"
|
||||
@echo " - lint-md lint markdown files"
|
||||
@echo " - lint-swagger lint swagger files"
|
||||
@echo " - lint-templates lint template files"
|
||||
@echo " - lint-yaml lint yaml files"
|
||||
@echo " - lint-spell lint spelling"
|
||||
@echo " - lint-spell-fix lint spelling and fix issues"
|
||||
@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 " - test-e2e[\#TestSpecificName] test end to end using playwright"
|
||||
@echo " - update update js and py dependencies"
|
||||
@echo " - update-js update js dependencies"
|
||||
@echo " - update-py update py dependencies"
|
||||
@echo " - webpack build webpack files"
|
||||
@echo " - svg build svg files"
|
||||
@echo " - fomantic build fomantic files"
|
||||
@echo " - generate run \"go generate\""
|
||||
@echo " - fmt format the Go code"
|
||||
@echo " - generate-license update license files"
|
||||
@echo " - generate-gitignore update gitignore files"
|
||||
@echo " - generate-manpage generate manpage"
|
||||
@echo " - generate-swagger generate the swagger spec from code comments"
|
||||
@echo " - swagger-validate check if the swagger spec is valid"
|
||||
@echo " - go-licenses regenerate go licenses"
|
||||
@echo " - tidy run go mod tidy"
|
||||
@echo " - test[\#TestSpecificName] run unit test"
|
||||
@echo " - test-sqlite[\#TestSpecificName] run integration test for sqlite"
|
||||
help: Makefile ## print Makefile help information.
|
||||
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m[TARGETS] default target: build\033[0m\n\n\033[35mTargets:\033[0m\n"} /^[0-9A-Za-z._-]+:.*?##/ { printf " \033[36m%-45s\033[0m %s\n", $$1, $$2 }' Makefile #$(MAKEFILE_LIST)
|
||||
@printf " \033[36m%-46s\033[0m %s\n" "test-e2e[#TestSpecificName]" "test end to end using playwright"
|
||||
@printf " \033[36m%-46s\033[0m %s\n" "test[#TestSpecificName]" "run unit test"
|
||||
@printf " \033[36m%-46s\033[0m %s\n" "test-sqlite[#TestSpecificName]" "run integration test for sqlite"
|
||||
|
||||
.PHONY: go-check
|
||||
go-check:
|
||||
|
@ -280,11 +221,11 @@ node-check:
|
|||
fi
|
||||
|
||||
.PHONY: clean-all
|
||||
clean-all: clean
|
||||
clean-all: clean ## delete backend, frontend and integration files
|
||||
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
clean: ## delete backend and integration files
|
||||
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) $(BINDATA_HASH) \
|
||||
integrations*.test \
|
||||
e2e*.test \
|
||||
|
@ -296,7 +237,7 @@ clean:
|
|||
tests/e2e/reports/ tests/e2e/test-artifacts/ tests/e2e/test-snapshots/
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
fmt: ## format the Go code
|
||||
@GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
|
||||
$(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl'))
|
||||
@# strip whitespace after '{{' or '(' and before '}}' or ')' unless there is only
|
||||
|
@ -311,7 +252,7 @@ fmt-check: fmt
|
|||
@diff=$$(git diff --color=always $(GO_SOURCES) templates $(WEB_DIRS)); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make fmt' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
printf "%s" "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
|
@ -325,95 +266,95 @@ TAGS_PREREQ := $(TAGS_EVIDENCE)
|
|||
endif
|
||||
|
||||
.PHONY: generate-swagger
|
||||
generate-swagger: $(SWAGGER_SPEC)
|
||||
generate-swagger: $(SWAGGER_SPEC) ## generate the swagger spec from code comments
|
||||
|
||||
$(SWAGGER_SPEC): $(GO_SOURCES_NO_BINDATA)
|
||||
$(GO) run $(SWAGGER_PACKAGE) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
|
||||
$(SWAGGER_SPEC): $(GO_SOURCES_NO_BINDATA) $(SWAGGER_SPEC_INPUT)
|
||||
$(GO) run $(SWAGGER_PACKAGE) generate spec --exclude "$(SWAGGER_EXCLUDE)" --input "$(SWAGGER_SPEC_INPUT)" --output './$(SWAGGER_SPEC)'
|
||||
|
||||
.PHONY: swagger-check
|
||||
swagger-check: generate-swagger
|
||||
@diff=$$(git diff --color=always '$(SWAGGER_SPEC)'); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make generate-swagger' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
printf "%s" "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: swagger-validate
|
||||
swagger-validate:
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)'
|
||||
swagger-validate: ## check if the swagger spec is valid
|
||||
@# swagger "validate" requires that the "basePath" must start with a slash, but we are using Golang template "{{...}}"
|
||||
@$(SED_INPLACE) -E -e 's|"basePath":( *)"(.*)"|"basePath":\1"/\2"|g' './$(SWAGGER_SPEC)' # add a prefix slash to basePath
|
||||
@# FIXME: there are some warnings
|
||||
$(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||
@$(SED_INPLACE) -E -e 's|"basePath":( *)"/(.*)"|"basePath":\1"\2"|g' './$(SWAGGER_SPEC)' # remove the prefix slash from basePath
|
||||
|
||||
.PHONY: checks
|
||||
checks: checks-frontend checks-backend
|
||||
checks: checks-frontend checks-backend ## run various consistency checks
|
||||
|
||||
.PHONY: checks-frontend
|
||||
checks-frontend: lockfile-check svg-check
|
||||
checks-frontend: lockfile-check svg-check ## check frontend files
|
||||
|
||||
.PHONY: checks-backend
|
||||
checks-backend: tidy-check swagger-check fmt-check swagger-validate security-check
|
||||
checks-backend: tidy-check swagger-check fmt-check swagger-validate security-check ## check backend files
|
||||
|
||||
.PHONY: lint
|
||||
lint: lint-frontend lint-backend lint-spell
|
||||
lint: lint-frontend lint-backend lint-spell ## lint everything
|
||||
|
||||
.PHONY: lint-fix
|
||||
lint-fix: lint-frontend-fix lint-backend-fix lint-spell-fix
|
||||
lint-fix: lint-frontend-fix lint-backend-fix lint-spell-fix ## lint everything and fix issues
|
||||
|
||||
.PHONY: lint-frontend
|
||||
lint-frontend: lint-js lint-css
|
||||
lint-frontend: lint-js lint-css ## lint frontend files
|
||||
|
||||
.PHONY: lint-frontend-fix
|
||||
lint-frontend-fix: lint-js-fix lint-css-fix
|
||||
lint-frontend-fix: lint-js-fix lint-css-fix ## lint frontend files and fix issues
|
||||
|
||||
.PHONY: lint-backend
|
||||
lint-backend: lint-go lint-go-vet lint-go-gopls lint-editorconfig
|
||||
lint-backend: lint-go lint-go-gitea-vet lint-go-gopls lint-editorconfig ## lint backend files
|
||||
|
||||
.PHONY: lint-backend-fix
|
||||
lint-backend-fix: lint-go-fix lint-go-vet lint-editorconfig
|
||||
lint-backend-fix: lint-go-fix lint-go-gitea-vet lint-editorconfig ## lint backend files and fix issues
|
||||
|
||||
.PHONY: lint-js
|
||||
lint-js: node_modules
|
||||
lint-js: node_modules ## lint js files
|
||||
npx eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES)
|
||||
# npx tsc
|
||||
npx vue-tsc
|
||||
|
||||
.PHONY: lint-js-fix
|
||||
lint-js-fix: node_modules
|
||||
lint-js-fix: node_modules ## lint js files and fix issues
|
||||
npx eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES) --fix
|
||||
# npx tsc
|
||||
npx vue-tsc
|
||||
|
||||
.PHONY: lint-css
|
||||
lint-css: node_modules
|
||||
lint-css: node_modules ## lint css files
|
||||
npx stylelint --color --max-warnings=0 $(STYLELINT_FILES)
|
||||
|
||||
.PHONY: lint-css-fix
|
||||
lint-css-fix: node_modules
|
||||
lint-css-fix: node_modules ## lint css files and fix issues
|
||||
npx stylelint --color --max-warnings=0 $(STYLELINT_FILES) --fix
|
||||
|
||||
.PHONY: lint-swagger
|
||||
lint-swagger: node_modules
|
||||
lint-swagger: node_modules ## lint swagger files
|
||||
npx spectral lint -q -F hint $(SWAGGER_SPEC)
|
||||
|
||||
.PHONY: lint-md
|
||||
lint-md: node_modules
|
||||
lint-md: node_modules ## lint markdown files
|
||||
npx markdownlint *.md
|
||||
|
||||
.PHONY: lint-spell
|
||||
lint-spell:
|
||||
lint-spell: ## lint spelling
|
||||
@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -error $(SPELLCHECK_FILES)
|
||||
|
||||
.PHONY: lint-spell-fix
|
||||
lint-spell-fix:
|
||||
lint-spell-fix: ## lint spelling and fix issues
|
||||
@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -w $(SPELLCHECK_FILES)
|
||||
|
||||
.PHONY: lint-go
|
||||
lint-go:
|
||||
lint-go: ## lint go files
|
||||
$(GO) run $(GOLANGCI_LINT_PACKAGE) run
|
||||
|
||||
.PHONY: lint-go-fix
|
||||
lint-go-fix:
|
||||
lint-go-fix: ## lint go files and fix issues
|
||||
$(GO) run $(GOLANGCI_LINT_PACKAGE) run --fix
|
||||
|
||||
# workaround step for the lint-go-windows CI task because 'go run' can not
|
||||
|
@ -423,57 +364,58 @@ lint-go-windows:
|
|||
@GOOS= GOARCH= $(GO) install $(GOLANGCI_LINT_PACKAGE)
|
||||
golangci-lint run
|
||||
|
||||
.PHONY: lint-go-vet
|
||||
lint-go-vet:
|
||||
@echo "Running go vet..."
|
||||
.PHONY: lint-go-gitea-vet
|
||||
lint-go-gitea-vet: ## lint go files with gitea-vet
|
||||
@echo "Running gitea-vet..."
|
||||
@GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet
|
||||
@$(GO) vet -vettool=gitea-vet ./...
|
||||
|
||||
.PHONY: lint-go-gopls
|
||||
lint-go-gopls:
|
||||
lint-go-gopls: ## lint go files with gopls
|
||||
@echo "Running gopls check..."
|
||||
@GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES_NO_BINDATA)
|
||||
|
||||
.PHONY: lint-editorconfig
|
||||
lint-editorconfig:
|
||||
@echo "Running editorconfig check..."
|
||||
@$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) $(EDITORCONFIG_FILES)
|
||||
|
||||
.PHONY: lint-actions
|
||||
lint-actions:
|
||||
lint-actions: ## lint action workflow files
|
||||
$(GO) run $(ACTIONLINT_PACKAGE)
|
||||
|
||||
.PHONY: lint-templates
|
||||
lint-templates: .venv node_modules
|
||||
lint-templates: .venv node_modules ## lint template files
|
||||
@node tools/lint-templates-svg.js
|
||||
@poetry run djlint $(shell find templates -type f -iname '*.tmpl')
|
||||
|
||||
.PHONY: lint-yaml
|
||||
lint-yaml: .venv
|
||||
@poetry run yamllint .
|
||||
lint-yaml: .venv ## lint yaml files
|
||||
@poetry run yamllint -s .
|
||||
|
||||
.PHONY: watch
|
||||
watch:
|
||||
watch: ## watch everything and continuously rebuild
|
||||
@bash tools/watch.sh
|
||||
|
||||
.PHONY: watch-frontend
|
||||
watch-frontend: node-check node_modules
|
||||
watch-frontend: node-check node_modules ## watch frontend files and continuously rebuild
|
||||
@rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||
NODE_ENV=development npx webpack --watch --progress
|
||||
|
||||
.PHONY: watch-backend
|
||||
watch-backend: go-check
|
||||
watch-backend: go-check ## watch backend files and continuously rebuild
|
||||
GITEA_RUN_MODE=dev $(GO) run $(AIR_PACKAGE) -c .air.toml
|
||||
|
||||
.PHONY: test
|
||||
test: test-frontend test-backend
|
||||
test: test-frontend test-backend ## test everything
|
||||
|
||||
.PHONY: test-backend
|
||||
test-backend:
|
||||
test-backend: ## test frontend files
|
||||
@echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
|
||||
@$(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_PACKAGES)
|
||||
|
||||
.PHONY: test-frontend
|
||||
test-frontend: node_modules
|
||||
test-frontend: node_modules ## test backend files
|
||||
npx vitest
|
||||
|
||||
.PHONY: test-check
|
||||
|
@ -482,7 +424,7 @@ test-check:
|
|||
@diff=$$(git status -s); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "make test-backend has changed files in the source tree:"; \
|
||||
echo "$${diff}"; \
|
||||
printf "%s" "$${diff}"; \
|
||||
echo "You should change the tests to create these files in a temporary directory."; \
|
||||
echo "Do not simply add these files to .gitignore"; \
|
||||
exit 1; \
|
||||
|
@ -505,7 +447,7 @@ unit-test-coverage:
|
|||
@$(GO) test $(GOTESTFLAGS) -timeout=20m -tags='$(TEST_TAGS)' -cover -coverprofile coverage.out $(GO_TEST_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
|
||||
|
||||
.PHONY: tidy
|
||||
tidy:
|
||||
tidy: ## run go mod 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)
|
||||
@$(MAKE) --no-print-directory $(GO_LICENSE_FILE)
|
||||
|
@ -519,15 +461,17 @@ tidy-check: tidy
|
|||
@diff=$$(git diff --color=always go.mod go.sum $(GO_LICENSE_FILE)); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make tidy' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
printf "%s" "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: go-licenses
|
||||
go-licenses: $(GO_LICENSE_FILE)
|
||||
go-licenses: $(GO_LICENSE_FILE) ## regenerate go licenses
|
||||
|
||||
$(GO_LICENSE_FILE): go.mod go.sum
|
||||
-$(GO) run $(GO_LICENSES_PACKAGE) save . --force --save_path=$(GO_LICENSE_TMP_DIR) 2>/dev/null
|
||||
@rm -rf $(GO_LICENSE_FILE)
|
||||
$(GO) install $(GO_LICENSES_PACKAGE)
|
||||
-GOOS=linux CGO_ENABLED=1 go-licenses save . --force --save_path=$(GO_LICENSE_TMP_DIR) 2>/dev/null
|
||||
$(GO) run build/generate-go-licenses.go $(GO_LICENSE_TMP_DIR) $(GO_LICENSE_FILE)
|
||||
@rm -rf $(GO_LICENSE_TMP_DIR)
|
||||
|
||||
|
@ -771,17 +715,17 @@ install: $(wildcard *.go)
|
|||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
|
||||
|
||||
.PHONY: build
|
||||
build: frontend backend
|
||||
build: frontend backend ## build everything
|
||||
|
||||
.PHONY: frontend
|
||||
frontend: $(WEBPACK_DEST)
|
||||
frontend: $(WEBPACK_DEST) ## build frontend files
|
||||
|
||||
.PHONY: backend
|
||||
backend: go-check generate-backend $(EXECUTABLE)
|
||||
backend: go-check generate-backend $(EXECUTABLE) ## build backend files
|
||||
|
||||
# We generate the backend before the frontend in case we in future we want to generate things in the frontend from generated files in backend
|
||||
.PHONY: generate
|
||||
generate: generate-backend
|
||||
generate: generate-backend ## run "go generate"
|
||||
|
||||
.PHONY: generate-backend
|
||||
generate-backend: $(TAGS_PREREQ) generate-go
|
||||
|
@ -806,22 +750,22 @@ $(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 'osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo $(TAGS)' -ldflags '-s -w -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 'osusergo gogit $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo gogit $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
|
||||
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) .
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
|
||||
|
||||
.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) .
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w $(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
|
||||
|
||||
.PHONY: release-freebsd
|
||||
release-freebsd: | $(DIST_DIRS)
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'freebsd/amd64' -out gitea-$(VERSION) .
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w $(LDFLAGS)' -targets 'freebsd/amd64' -out gitea-$(VERSION) .
|
||||
|
||||
.PHONY: release-copy
|
||||
release-copy: | $(DIST_DIRS)
|
||||
|
@ -846,20 +790,20 @@ release-sources: | $(DIST_DIRS)
|
|||
rm -f $(STORED_VERSION_FILE)
|
||||
|
||||
.PHONY: deps
|
||||
deps: deps-frontend deps-backend deps-tools deps-py
|
||||
deps: deps-frontend deps-backend deps-tools deps-py ## install dependencies
|
||||
|
||||
.PHONY: deps-py
|
||||
deps-py: .venv
|
||||
deps-py: .venv ## install python dependencies
|
||||
|
||||
.PHONY: deps-frontend
|
||||
deps-frontend: node_modules
|
||||
deps-frontend: node_modules ## install frontend dependencies
|
||||
|
||||
.PHONY: deps-backend
|
||||
deps-backend:
|
||||
deps-backend: ## install backend dependencies
|
||||
$(GO) mod download
|
||||
|
||||
.PHONY: deps-tools
|
||||
deps-tools:
|
||||
deps-tools: ## install tool dependencies
|
||||
$(GO) install $(AIR_PACKAGE) & \
|
||||
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE) & \
|
||||
$(GO) install $(GOFUMPT_PACKAGE) & \
|
||||
|
@ -883,10 +827,10 @@ node_modules: package-lock.json
|
|||
@touch .venv
|
||||
|
||||
.PHONY: update
|
||||
update: update-js update-py
|
||||
update: update-js update-py ## update js and py dependencies
|
||||
|
||||
.PHONY: update-js
|
||||
update-js: node-check | node_modules
|
||||
update-js: node-check | node_modules ## update js dependencies
|
||||
npx updates -u -f package.json
|
||||
rm -rf node_modules package-lock.json
|
||||
npm install --package-lock
|
||||
|
@ -895,27 +839,14 @@ update-js: node-check | node_modules
|
|||
@touch node_modules
|
||||
|
||||
.PHONY: update-py
|
||||
update-py: node-check | node_modules
|
||||
update-py: node-check | node_modules ## update py dependencies
|
||||
npx updates -u -f pyproject.toml
|
||||
rm -rf .venv poetry.lock
|
||||
poetry install
|
||||
@touch .venv
|
||||
|
||||
.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/
|
||||
$(SED_INPLACE) -e 's/ overrideBrowserslist\r/ overrideBrowserslist: ["defaults"]\r/g' $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/tasks/config/tasks.js
|
||||
cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build
|
||||
# fomantic uses "touchstart" as click event for some browsers, it's not ideal, so we force fomantic to always use "click" as click event
|
||||
$(SED_INPLACE) -e 's/clickEvent[ \t]*=/clickEvent = "click", unstableClickEvent =/g' $(FOMANTIC_WORK_DIR)/build/semantic.js
|
||||
$(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.*
|
||||
|
||||
.PHONY: webpack
|
||||
webpack: $(WEBPACK_DEST)
|
||||
webpack: $(WEBPACK_DEST) ## build webpack files
|
||||
|
||||
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
|
||||
@$(MAKE) -s node-check node_modules
|
||||
|
@ -925,7 +856,7 @@ $(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
|
|||
@touch $(WEBPACK_DEST)
|
||||
|
||||
.PHONY: svg
|
||||
svg: node-check | node_modules
|
||||
svg: node-check | node_modules ## build svg files
|
||||
rm -rf $(SVG_DEST_DIR)
|
||||
node tools/generate-svg.js
|
||||
|
||||
|
@ -935,7 +866,7 @@ svg-check: svg
|
|||
@diff=$$(git diff --color=always --cached $(SVG_DEST_DIR)); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make svg' and 'git add $(SVG_DEST_DIR)' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
printf "%s" "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
|
@ -946,7 +877,7 @@ lockfile-check:
|
|||
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}"; \
|
||||
printf "%s" "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
|
@ -960,12 +891,8 @@ update-translations:
|
|||
mv ./translations/*.ini ./options/locale/
|
||||
rmdir ./translations
|
||||
|
||||
.PHONY: generate-license
|
||||
generate-license:
|
||||
$(GO) run build/generate-licenses.go
|
||||
|
||||
.PHONY: generate-gitignore
|
||||
generate-gitignore:
|
||||
generate-gitignore: ## update gitignore files
|
||||
$(GO) run build/generate-gitignores.go
|
||||
|
||||
.PHONY: generate-images
|
||||
|
@ -974,7 +901,7 @@ generate-images: | node_modules
|
|||
node tools/generate-images.js $(TAGS)
|
||||
|
||||
.PHONY: generate-manpage
|
||||
generate-manpage:
|
||||
generate-manpage: ## generate manpage
|
||||
@[ -f gitea ] || make backend
|
||||
@mkdir -p man/man1/ man/man5
|
||||
@./gitea docs --man > man/man1/gitea.1
|
||||
|
|
106
README.md
106
README.md
|
@ -9,7 +9,7 @@
|
|||
[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
|
||||
[](https://opensource.org/licenses/MIT "License: MIT")
|
||||
[](https://gitpod.io/#https://github.com/go-gitea/gitea)
|
||||
[](https://crowdin.com/project/gitea "Crowdin")
|
||||
[](https://translate.gitea.com "Crowdin")
|
||||
|
||||
[View this document in Chinese](./README_ZH.md)
|
||||
|
||||
|
@ -31,6 +31,14 @@ For accessing free Gitea service (with a limited number of repositories), you ca
|
|||
|
||||
To quickly deploy your own dedicated Gitea instance on Gitea Cloud, you can start a free trial at [cloud.gitea.com](https://cloud.gitea.com).
|
||||
|
||||
## Documentation
|
||||
|
||||
You can find comprehensive documentation on our official [documentation website](https://docs.gitea.com/).
|
||||
|
||||
It includes installation, administration, usage, development, contributing guides, and more to help you get started and explore all features effectively.
|
||||
|
||||
If you have any suggestions or would like to contribute to it, you can visit the [documentation repository](https://gitea.com/gitea/docs)
|
||||
|
||||
## Building
|
||||
|
||||
From the root of the source tree, run:
|
||||
|
@ -52,6 +60,8 @@ More info: https://docs.gitea.com/installation/install-from-source
|
|||
|
||||
## Using
|
||||
|
||||
After building, a binary file named `gitea` will be generated in the root of the source tree by default. To run it, use:
|
||||
|
||||
./gitea web
|
||||
|
||||
> [!NOTE]
|
||||
|
@ -68,22 +78,25 @@ Expected workflow is: Fork -> Patch -> Push -> Pull Request
|
|||
|
||||
## 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.
|
||||
[](https://translate.gitea.com)
|
||||
|
||||
Translations are done through [Crowdin](https://translate.gitea.com). 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 to fill it as questions pop up.
|
||||
|
||||
https://docs.gitea.com/contributing/localization
|
||||
Get more information from [documentation](https://docs.gitea.com/contributing/localization).
|
||||
|
||||
[](https://crowdin.com/project/gitea)
|
||||
## Official and Third-Party Projects
|
||||
|
||||
## Further information
|
||||
We provide an official [go-sdk](https://gitea.com/gitea/go-sdk), a CLI tool called [tea](https://gitea.com/gitea/tea) and an [action runner](https://gitea.com/gitea/act_runner) for Gitea Action.
|
||||
|
||||
For more information and instructions about how to install Gitea, please look at our [documentation](https://docs.gitea.com/).
|
||||
If you have questions that are not covered by the documentation, you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create a post in the [discourse forum](https://forum.gitea.com/).
|
||||
We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea), where you can discover more third-party projects, including SDKs, plugins, themes, and more.
|
||||
|
||||
We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea).
|
||||
## Communication
|
||||
|
||||
The official Gitea CLI is developed at [gitea/tea](https://gitea.com/gitea/tea).
|
||||
[](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
|
||||
|
||||
If you have questions that are not covered by the [documentation](https://docs.gitea.com/), you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create a post in the [discourse forum](https://forum.gitea.com/).
|
||||
|
||||
## Authors
|
||||
|
||||
|
@ -122,18 +135,79 @@ Gitea is pronounced [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY) as in "gi-tea"
|
|||
|
||||
We're [working on it](https://github.com/go-gitea/gitea/issues/1029).
|
||||
|
||||
**Where can I find the security patches?**
|
||||
|
||||
In the [release log](https://github.com/go-gitea/gitea/releases) or the [change log](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md), search for the keyword `SECURITY` to find the security patches.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License.
|
||||
See the [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) file
|
||||
for the full license text.
|
||||
|
||||
## Screenshots
|
||||
## Further information
|
||||
|
||||
Looking for an overview of the interface? Check it out!
|
||||
<details>
|
||||
<summary>Looking for an overview of the interface? Check it out!</summary>
|
||||
|
||||
||||
|
||||
|:---:|:---:|:---:|
|
||||
||||
|
||||
||||
|
||||
||||
|
||||
### Login/Register Page
|
||||
|
||||

|
||||

|
||||
|
||||
### User Dashboard
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
### User Profile
|
||||
|
||||

|
||||
|
||||
### Explore
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
### Repository
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
#### Repository Issue
|
||||
|
||||

|
||||

|
||||
|
||||
#### Repository Pull Requests
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
#### Repository Actions
|
||||
|
||||

|
||||

|
||||
|
||||
#### Repository Activity
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
### Organization
|
||||
|
||||

|
||||
|
||||
</details>
|
||||
|
|
137
README_ZH.md
137
README_ZH.md
|
@ -9,13 +9,13 @@
|
|||
[](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
|
||||
[](https://opensource.org/licenses/MIT "License: MIT")
|
||||
[](https://gitpod.io/#https://github.com/go-gitea/gitea)
|
||||
[](https://crowdin.com/project/gitea "Crowdin")
|
||||
[](https://translate.gitea.com "Crowdin")
|
||||
|
||||
[View this document in English](./README.md)
|
||||
|
||||
## 目标
|
||||
|
||||
Gitea 的首要目标是创建一个极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。我们采用 Go 作为后端语言,这使我们只要生成一个可执行程序即可。并且他还支持跨平台,支持 Linux, macOS 和 Windows 以及各种架构,除了 x86,amd64,还包括 ARM 和 PowerPC。
|
||||
Gitea 的首要目标是创建一个极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。我们采用 Go 作为后端语言,这使我们只要生成一个可执行程序即可。并且他还支持跨平台,支持 Linux、macOS 和 Windows 以及各种架构,除了 x86 和 amd64,还包括 ARM 和 PowerPC。
|
||||
|
||||
如果你想试用在线演示和报告问题,请访问 [demo.gitea.com](https://demo.gitea.com/)。
|
||||
|
||||
|
@ -23,39 +23,134 @@ Gitea 的首要目标是创建一个极易安装,运行非常快速,安装
|
|||
|
||||
如果你想在 Gitea Cloud 上快速部署你自己独享的 Gitea 实例,请访问 [cloud.gitea.com](https://cloud.gitea.com) 开始免费试用。
|
||||
|
||||
## 提示
|
||||
|
||||
1. **开始贡献代码之前请确保你已经看过了 [贡献者向导(英文)](CONTRIBUTING.md)**.
|
||||
2. 所有的安全问题,请私下发送邮件给 **security@gitea.io**。谢谢!
|
||||
3. 如果你要使用API,请参见 [API 文档](https://godoc.org/code.gitea.io/sdk/gitea).
|
||||
|
||||
## 文档
|
||||
|
||||
关于如何安装请访问我们的 [文档站](https://docs.gitea.com/zh-cn/category/installation),如果没有找到对应的文档,你也可以通过 [Discord - 英文](https://discord.gg/gitea) 和 QQ群 328432459 来和我们交流。
|
||||
|
||||
## 贡献流程
|
||||
## 编译
|
||||
|
||||
Fork -> Patch -> Push -> Pull Request
|
||||
在源代码的根目录下执行:
|
||||
|
||||
TAGS="bindata" make build
|
||||
|
||||
或者如果需要SQLite支持:
|
||||
|
||||
TAGS="bindata sqlite sqlite_unlock_notify" make build
|
||||
|
||||
编译过程会分成2个子任务:
|
||||
|
||||
- `make backend`,需要 [Go Stable](https://go.dev/dl/),最低版本需求可查看 [go.mod](/go.mod)。
|
||||
- `make frontend`,需要 [Node.js LTS](https://nodejs.org/en/download/) 或更高版本。
|
||||
|
||||
你需要连接网络来下载 go 和 npm modules。当从 tar 格式的源文件编译时,其中包含了预编译的前端文件,因此 `make frontend` 将不会被执行。这允许编译时不需要 Node.js。
|
||||
|
||||
更多信息: https://docs.gitea.com/installation/install-from-source
|
||||
|
||||
## 使用
|
||||
|
||||
编译之后,默认会在根目录下生成一个名为 `gitea` 的文件。你可以这样执行它:
|
||||
|
||||
./gitea web
|
||||
|
||||
> [!注意]
|
||||
> 如果你要使用API,请参见 [API 文档](https://godoc.org/code.gitea.io/sdk/gitea)。
|
||||
|
||||
## 贡献
|
||||
|
||||
贡献流程:Fork -> Patch -> Push -> Pull Request
|
||||
|
||||
> [!注意]
|
||||
>
|
||||
> 1. **开始贡献代码之前请确保你已经看过了 [贡献者向导(英文)](CONTRIBUTING.md)**。
|
||||
> 2. 所有的安全问题,请私下发送邮件给 **security@gitea.io**。 谢谢!
|
||||
|
||||
## 翻译
|
||||
|
||||
多语言翻译是基于Crowdin进行的.
|
||||
[](https://crowdin.com/project/gitea)
|
||||
[](https://translate.gitea.com)
|
||||
|
||||
多语言翻译是基于Crowdin进行的。
|
||||
|
||||
从 [文档](https://docs.gitea.com/contributing/localization) 中获取更多信息。
|
||||
|
||||
## 官方和第三方项目
|
||||
|
||||
Gitea 提供官方的 [go-sdk](https://gitea.com/gitea/go-sdk),以及名为 [tea](https://gitea.com/gitea/tea) 的 CLI 工具 和 用于 Gitea Action 的 [action runner](https://gitea.com/gitea/act_runner)。
|
||||
|
||||
[gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 是一个 Gitea 相关项目的列表,你可以在这里找到更多的第三方项目,包括 SDK、插件、主题等等。
|
||||
|
||||
## 作者
|
||||
|
||||
* [Maintainers](https://github.com/orgs/go-gitea/people)
|
||||
* [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
|
||||
* [Translators](options/locale/TRANSLATORS)
|
||||
- [Maintainers](https://github.com/orgs/go-gitea/people)
|
||||
- [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
|
||||
- [Translators](options/locale/TRANSLATORS)
|
||||
|
||||
## 授权许可
|
||||
|
||||
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件中。
|
||||
|
||||
## 截图
|
||||
## 更多信息
|
||||
|
||||
||||
|
||||
|:---:|:---:|:---:|
|
||||
||||
|
||||
||||
|
||||
||||
|
||||
<details>
|
||||
<summary>截图</summary>
|
||||
|
||||
### 登录界面
|
||||
|
||||

|
||||

|
||||
|
||||
### 用户首页
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
### 用户资料
|
||||
|
||||

|
||||
|
||||
### 探索
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
### 仓库
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
#### 仓库工单
|
||||
|
||||

|
||||

|
||||
|
||||
#### 仓库合并请求
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
#### 仓库 Actions
|
||||
|
||||

|
||||

|
||||
|
||||
#### 仓库动态
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
### 组织
|
||||
|
||||

|
||||
|
||||
</details>
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -53,8 +53,6 @@ func (e Emoji) MarshalJSON() ([]byte, error) {
|
|||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
|
||||
flag.Parse()
|
||||
|
||||
// generate data
|
||||
|
@ -83,8 +81,6 @@ var replacer = strings.NewReplacer(
|
|||
var emojiRE = regexp.MustCompile(`\{Emoji:"([^"]*)"`)
|
||||
|
||||
func generate() ([]byte, error) {
|
||||
var err error
|
||||
|
||||
// load gemoji data
|
||||
res, err := http.Get(gemojiURL)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,176 +0,0 @@
|
|||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/build/license"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
prefix = "gitea-licenses"
|
||||
url = "https://api.github.com/repos/spdx/license-list-data/tarball"
|
||||
githubApiToken = ""
|
||||
githubUsername = ""
|
||||
destination = ""
|
||||
)
|
||||
|
||||
flag.StringVar(&destination, "dest", "options/license/", "destination for the licenses")
|
||||
flag.StringVar(&githubUsername, "username", "", "github username")
|
||||
flag.StringVar(&githubApiToken, "token", "", "github api token")
|
||||
flag.Parse()
|
||||
|
||||
file, err := os.CreateTemp(os.TempDir(), prefix)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create temp file. %s", err)
|
||||
}
|
||||
|
||||
defer util.Remove(file.Name())
|
||||
|
||||
if err := os.RemoveAll(destination); err != nil {
|
||||
log.Fatalf("Cannot clean destination folder: %v", err)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(destination, 0o755); err != nil {
|
||||
log.Fatalf("Cannot create destination: %v", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to download archive. %s", err)
|
||||
}
|
||||
|
||||
if len(githubApiToken) > 0 && len(githubUsername) > 0 {
|
||||
req.SetBasicAuth(githubUsername, githubApiToken)
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to download archive. %s", err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if _, err := io.Copy(file, resp.Body); err != nil {
|
||||
log.Fatalf("Failed to copy archive to file. %s", err)
|
||||
}
|
||||
|
||||
if _, err := file.Seek(0, 0); err != nil {
|
||||
log.Fatalf("Failed to reset seek on archive. %s", err)
|
||||
}
|
||||
|
||||
gz, err := gzip.NewReader(file)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to gunzip the archive. %s", err)
|
||||
}
|
||||
|
||||
tr := tar.NewReader(gz)
|
||||
aliasesFiles := make(map[string][]string)
|
||||
for {
|
||||
hdr, err := tr.Next()
|
||||
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to iterate archive. %s", err)
|
||||
}
|
||||
|
||||
if !strings.Contains(hdr.Name, "/text/") {
|
||||
continue
|
||||
}
|
||||
|
||||
if filepath.Ext(hdr.Name) != ".txt" {
|
||||
continue
|
||||
}
|
||||
|
||||
fileBaseName := filepath.Base(hdr.Name)
|
||||
licenseName := strings.TrimSuffix(fileBaseName, ".txt")
|
||||
|
||||
if strings.HasPrefix(fileBaseName, "README") {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(fileBaseName, "deprecated_") {
|
||||
continue
|
||||
}
|
||||
out, err := os.Create(path.Join(destination, licenseName))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create new file. %s", err)
|
||||
}
|
||||
|
||||
defer out.Close()
|
||||
|
||||
// some license files have same content, so we need to detect these files and create a convert map into a json file
|
||||
// Later we use this convert map to avoid adding same license content with different license name
|
||||
h := md5.New()
|
||||
// calculate md5 and write file in the same time
|
||||
r := io.TeeReader(tr, h)
|
||||
if _, err := io.Copy(out, r); err != nil {
|
||||
log.Fatalf("Failed to write new file. %s", err)
|
||||
} else {
|
||||
fmt.Printf("Written %s\n", out.Name())
|
||||
|
||||
md5 := hex.EncodeToString(h.Sum(nil))
|
||||
aliasesFiles[md5] = append(aliasesFiles[md5], licenseName)
|
||||
}
|
||||
}
|
||||
|
||||
// generate convert license name map
|
||||
licenseAliases := make(map[string]string)
|
||||
for _, fileNames := range aliasesFiles {
|
||||
if len(fileNames) > 1 {
|
||||
licenseName := license.GetLicenseNameFromAliases(fileNames)
|
||||
if licenseName == "" {
|
||||
// license name should not be empty as expected
|
||||
// if it is empty, we need to rewrite the logic of GetLicenseNameFromAliases
|
||||
log.Fatalf("GetLicenseNameFromAliases: license name is empty")
|
||||
}
|
||||
for _, fileName := range fileNames {
|
||||
licenseAliases[fileName] = licenseName
|
||||
}
|
||||
}
|
||||
}
|
||||
// save convert license name map to file
|
||||
b, err := json.Marshal(licenseAliases)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create json bytes. %s", err)
|
||||
}
|
||||
|
||||
licenseAliasesDestination := filepath.Join(destination, "etc", "license-aliases.json")
|
||||
if err := os.MkdirAll(filepath.Dir(licenseAliasesDestination), 0o755); err != nil {
|
||||
log.Fatalf("Failed to create directory for license aliases json file. %s", err)
|
||||
}
|
||||
|
||||
f, err := os.Create(licenseAliasesDestination)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create license aliases json file. %s", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err = f.Write(b); err != nil {
|
||||
log.Fatalf("Failed to write license aliases json file. %s", err)
|
||||
}
|
||||
|
||||
fmt.Println("Done")
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package license
|
||||
|
||||
import "strings"
|
||||
|
||||
func GetLicenseNameFromAliases(fnl []string) string {
|
||||
if len(fnl) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
shortestItem := func(list []string) string {
|
||||
s := list[0]
|
||||
for _, l := range list[1:] {
|
||||
if len(l) < len(s) {
|
||||
s = l
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
allHasPrefix := func(list []string, s string) bool {
|
||||
for _, l := range list {
|
||||
if !strings.HasPrefix(l, s) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
sl := shortestItem(fnl)
|
||||
slv := strings.Split(sl, "-")
|
||||
var result string
|
||||
for i := len(slv); i >= 0; i-- {
|
||||
result = strings.Join(slv[:i], "-")
|
||||
if allHasPrefix(fnl, result) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package license
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetLicenseNameFromAliases(t *testing.T) {
|
||||
tests := []struct {
|
||||
target string
|
||||
inputs []string
|
||||
}{
|
||||
{
|
||||
// real case which you can find in license-aliases.json
|
||||
target: "AGPL-1.0",
|
||||
inputs: []string{
|
||||
"AGPL-1.0-only",
|
||||
"AGPL-1.0-or-late",
|
||||
},
|
||||
},
|
||||
{
|
||||
target: "",
|
||||
inputs: []string{
|
||||
"APSL-1.0",
|
||||
"AGPL-1.0-only",
|
||||
"AGPL-1.0-or-late",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
result := GetLicenseNameFromAliases(tt.inputs)
|
||||
assert.Equal(t, result, tt.target)
|
||||
}
|
||||
}
|
|
@ -31,6 +31,11 @@ var microcmdUserCreate = &cli.Command{
|
|||
Name: "username",
|
||||
Usage: "Username",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "user-type",
|
||||
Usage: "Set user's type: individual or bot",
|
||||
Value: "individual",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "password",
|
||||
Usage: "User password",
|
||||
|
@ -69,10 +74,30 @@ var microcmdUserCreate = &cli.Command{
|
|||
}
|
||||
|
||||
func runCreateUser(c *cli.Context) error {
|
||||
// this command highly depends on the many setting options (create org, visibility, etc.), so it must have a full setting load first
|
||||
// duplicate setting loading should be safe at the moment, but it should be refactored & improved in the future.
|
||||
setting.LoadSettings()
|
||||
|
||||
if err := argsSet(c, "email"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userTypes := map[string]user_model.UserType{
|
||||
"individual": user_model.UserTypeIndividual,
|
||||
"bot": user_model.UserTypeBot,
|
||||
}
|
||||
userType, ok := userTypes[c.String("user-type")]
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid user type: %s", c.String("user-type"))
|
||||
}
|
||||
if userType != user_model.UserTypeIndividual {
|
||||
// Some other commands like "change-password" also only support individual users.
|
||||
// It needs to clarify the "password" behavior for bot users in the future.
|
||||
// At the moment, we do not allow setting password for bot users.
|
||||
if c.IsSet("password") || c.IsSet("random-password") {
|
||||
return errors.New("password can only be set for individual users")
|
||||
}
|
||||
}
|
||||
if c.IsSet("name") && c.IsSet("username") {
|
||||
return errors.New("cannot set both --name and --username flags")
|
||||
}
|
||||
|
@ -114,16 +139,19 @@ func runCreateUser(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
fmt.Printf("generated random password is '%s'\n", password)
|
||||
} else {
|
||||
} else if userType == user_model.UserTypeIndividual {
|
||||
return errors.New("must set either password or random-password flag")
|
||||
}
|
||||
|
||||
isAdmin := c.Bool("admin")
|
||||
mustChangePassword := true // always default to true
|
||||
if c.IsSet("must-change-password") {
|
||||
if userType != user_model.UserTypeIndividual {
|
||||
return errors.New("must-change-password flag can only be set for individual users")
|
||||
}
|
||||
// if the flag is set, use the value provided by the user
|
||||
mustChangePassword = c.Bool("must-change-password")
|
||||
} else {
|
||||
} else if userType == user_model.UserTypeIndividual {
|
||||
// check whether there are users in the database
|
||||
hasUserRecord, err := db.IsTableNotEmpty(&user_model.User{})
|
||||
if err != nil {
|
||||
|
@ -147,8 +175,9 @@ func runCreateUser(c *cli.Context) error {
|
|||
u := &user_model.User{
|
||||
Name: username,
|
||||
Email: c.String("email"),
|
||||
Passwd: password,
|
||||
IsAdmin: isAdmin,
|
||||
Type: userType,
|
||||
Passwd: password,
|
||||
MustChangePassword: mustChangePassword,
|
||||
Visibility: visibility,
|
||||
}
|
||||
|
|
|
@ -13,32 +13,54 @@ import (
|
|||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAdminUserCreate(t *testing.T) {
|
||||
app := NewMainApp(AppVersion{})
|
||||
|
||||
reset := func() {
|
||||
assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
|
||||
assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
|
||||
require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
|
||||
require.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
|
||||
}
|
||||
|
||||
type createCheck struct{ IsAdmin, MustChangePassword bool }
|
||||
createUser := func(name, args string) createCheck {
|
||||
assert.NoError(t, app.Run(strings.Fields(fmt.Sprintf("./gitea admin user create --username %s --email %s@gitea.local %s --password foobar", name, name, args))))
|
||||
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: name})
|
||||
return createCheck{u.IsAdmin, u.MustChangePassword}
|
||||
}
|
||||
reset()
|
||||
assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: false}, createUser("u", ""), "first non-admin user doesn't need to change password")
|
||||
t.Run("MustChangePassword", func(t *testing.T) {
|
||||
type check struct {
|
||||
IsAdmin bool
|
||||
MustChangePassword bool
|
||||
}
|
||||
createCheck := func(name, args string) check {
|
||||
require.NoError(t, app.Run(strings.Fields(fmt.Sprintf("./gitea admin user create --username %s --email %s@gitea.local %s --password foobar", name, name, args))))
|
||||
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: name})
|
||||
return check{IsAdmin: u.IsAdmin, MustChangePassword: u.MustChangePassword}
|
||||
}
|
||||
reset()
|
||||
assert.Equal(t, check{IsAdmin: false, MustChangePassword: false}, createCheck("u", ""), "first non-admin user doesn't need to change password")
|
||||
|
||||
reset()
|
||||
assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: false}, createUser("u", "--admin"), "first admin user doesn't need to change password")
|
||||
reset()
|
||||
assert.Equal(t, check{IsAdmin: true, MustChangePassword: false}, createCheck("u", "--admin"), "first admin user doesn't need to change password")
|
||||
|
||||
reset()
|
||||
assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: true}, createUser("u", "--admin --must-change-password"))
|
||||
assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: true}, createUser("u2", "--admin"))
|
||||
assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: false}, createUser("u3", "--admin --must-change-password=false"))
|
||||
assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: true}, createUser("u4", ""))
|
||||
assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: false}, createUser("u5", "--must-change-password=false"))
|
||||
reset()
|
||||
assert.Equal(t, check{IsAdmin: true, MustChangePassword: true}, createCheck("u", "--admin --must-change-password"))
|
||||
assert.Equal(t, check{IsAdmin: true, MustChangePassword: true}, createCheck("u2", "--admin"))
|
||||
assert.Equal(t, check{IsAdmin: true, MustChangePassword: false}, createCheck("u3", "--admin --must-change-password=false"))
|
||||
assert.Equal(t, check{IsAdmin: false, MustChangePassword: true}, createCheck("u4", ""))
|
||||
assert.Equal(t, check{IsAdmin: false, MustChangePassword: false}, createCheck("u5", "--must-change-password=false"))
|
||||
})
|
||||
|
||||
t.Run("UserType", func(t *testing.T) {
|
||||
createUser := func(name, args string) error {
|
||||
return app.Run(strings.Fields(fmt.Sprintf("./gitea admin user create --username %s --email %s@gitea.local %s", name, name, args)))
|
||||
}
|
||||
|
||||
reset()
|
||||
assert.ErrorContains(t, createUser("u", "--user-type invalid"), "invalid user type")
|
||||
assert.ErrorContains(t, createUser("u", "--user-type bot --password 123"), "can only be set for individual users")
|
||||
assert.ErrorContains(t, createUser("u", "--user-type bot --must-change-password"), "can only be set for individual users")
|
||||
|
||||
assert.NoError(t, createUser("u", "--user-type bot"))
|
||||
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "u"})
|
||||
assert.Equal(t, user_model.UserTypeBot, u.Type)
|
||||
assert.Equal(t, "", u.Passwd)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
golog "log"
|
||||
"os"
|
||||
|
@ -130,8 +131,8 @@ func runRecreateTable(ctx *cli.Context) error {
|
|||
}
|
||||
recreateTables := migrate_base.RecreateTables(beans...)
|
||||
|
||||
return db.InitEngineWithMigration(stdCtx, func(x *xorm.Engine) error {
|
||||
if err := migrations.EnsureUpToDate(x); err != nil {
|
||||
return db.InitEngineWithMigration(stdCtx, func(ctx context.Context, x *xorm.Engine) error {
|
||||
if err := migrations.EnsureUpToDate(ctx, x); err != nil {
|
||||
return err
|
||||
}
|
||||
return recreateTables(x)
|
||||
|
|
|
@ -316,7 +316,7 @@ func runHookPostReceive(c *cli.Context) error {
|
|||
setup(ctx, 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").RunStdString(ctx, nil); err != nil {
|
||||
return fmt.Errorf("Failed to call 'git update-server-info': %w", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ package cmd
|
|||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -15,7 +14,7 @@ import (
|
|||
|
||||
func TestPktLine(t *testing.T) {
|
||||
// test read
|
||||
ctx := context.Background()
|
||||
ctx := t.Context()
|
||||
s := strings.NewReader("0000")
|
||||
r := bufio.NewReader(s)
|
||||
result, err := readPktLine(ctx, r, pktLineTypeFlush)
|
||||
|
|
|
@ -165,6 +165,7 @@ func NewMainApp(appVer AppVersion) *cli.App {
|
|||
app.Commands = append(app.Commands, subCmdWithConfig...)
|
||||
app.Commands = append(app.Commands, subCmdStandalone...)
|
||||
|
||||
setting.InitGiteaEnvVars()
|
||||
return app
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ package cmd
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -113,37 +112,17 @@ func TestCliCmd(t *testing.T) {
|
|||
_, _ = fmt.Fprint(ctx.App.Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
|
||||
return nil
|
||||
})
|
||||
var envBackup []string
|
||||
for _, s := range os.Environ() {
|
||||
if strings.HasPrefix(s, "GITEA_") && strings.Contains(s, "=") {
|
||||
envBackup = append(envBackup, s)
|
||||
}
|
||||
}
|
||||
clearGiteaEnv := func() {
|
||||
for _, s := range os.Environ() {
|
||||
if strings.HasPrefix(s, "GITEA_") {
|
||||
_ = os.Unsetenv(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
clearGiteaEnv()
|
||||
for _, s := range envBackup {
|
||||
k, v, _ := strings.Cut(s, "=")
|
||||
_ = os.Setenv(k, v)
|
||||
}
|
||||
}()
|
||||
|
||||
for _, c := range cases {
|
||||
clearGiteaEnv()
|
||||
for k, v := range c.env {
|
||||
_ = os.Setenv(k, v)
|
||||
}
|
||||
args := strings.Split(c.cmd, " ") // for test only, "split" is good enough
|
||||
r, err := runTestApp(app, args...)
|
||||
assert.NoError(t, err, c.cmd)
|
||||
assert.NotEmpty(t, c.exp, c.cmd)
|
||||
assert.Contains(t, r.Stdout, c.exp, c.cmd)
|
||||
t.Run(c.cmd, func(t *testing.T) {
|
||||
for k, v := range c.env {
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
args := strings.Split(c.cmd, " ") // for test only, "split" is good enough
|
||||
r, err := runTestApp(app, args...)
|
||||
assert.NoError(t, err, c.cmd)
|
||||
assert.NotEmpty(t, c.exp, c.cmd)
|
||||
assert.Contains(t, r.Stdout, c.exp, c.cmd)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@ import (
|
|||
"context"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/migrations"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/services/versioned_migration"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
@ -18,7 +18,7 @@ import (
|
|||
var CmdMigrate = &cli.Command{
|
||||
Name: "migrate",
|
||||
Usage: "Migrate the database",
|
||||
Description: "This is a command for migrating the database, so that you can run gitea admin create-user before starting the server.",
|
||||
Description: `This is a command for migrating the database, so that you can run "gitea admin create user" before starting the server.`,
|
||||
Action: runMigrate,
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ func runMigrate(ctx *cli.Context) error {
|
|||
log.Info("Log path: %s", setting.Log.RootPath)
|
||||
log.Info("Configuration file: %s", setting.CustomConf)
|
||||
|
||||
if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
|
||||
if err := db.InitEngineWithMigration(context.Background(), versioned_migration.Migrate); err != nil {
|
||||
log.Fatal("Failed to initialize ORM engine: %v", err)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import (
|
|||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
"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"
|
||||
|
@ -21,6 +20,7 @@ import (
|
|||
packages_module "code.gitea.io/gitea/modules/packages"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
"code.gitea.io/gitea/services/versioned_migration"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
@ -196,7 +196,7 @@ func migrateActionsLog(ctx context.Context, dstStorage storage.ObjectStorage) er
|
|||
|
||||
func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStorage) error {
|
||||
return db.Iterate(ctx, nil, func(ctx context.Context, artifact *actions_model.ActionArtifact) error {
|
||||
if artifact.Status == int64(actions_model.ArtifactStatusExpired) {
|
||||
if artifact.Status == actions_model.ArtifactStatusExpired {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -227,7 +227,7 @@ func runMigrateStorage(ctx *cli.Context) error {
|
|||
log.Info("Log path: %s", setting.Log.RootPath)
|
||||
log.Info("Configuration file: %s", setting.CustomConf)
|
||||
|
||||
if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
|
||||
if err := db.InitEngineWithMigration(context.Background(), versioned_migration.Migrate); err != nil {
|
||||
log.Fatal("Failed to initialize ORM engine: %v", err)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -53,7 +52,7 @@ func TestMigratePackages(t *testing.T) {
|
|||
assert.NotNil(t, v)
|
||||
assert.NotNil(t, f)
|
||||
|
||||
ctx := context.Background()
|
||||
ctx := t.Context()
|
||||
|
||||
p := t.TempDir()
|
||||
|
||||
|
|
19
cmd/serv.go
19
cmd/serv.go
|
@ -104,19 +104,20 @@ func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) 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)
|
||||
// add extra empty lines to separate our message from other git errors to get more attention
|
||||
_, _ = fmt.Fprintln(os.Stderr, "error:")
|
||||
_, _ = fmt.Fprintln(os.Stderr, "error:", userMessage)
|
||||
_, _ = fmt.Fprintln(os.Stderr, "error:")
|
||||
|
||||
if logMsgFmt != "" {
|
||||
logMsg := fmt.Sprintf(logMsgFmt, args...)
|
||||
if !setting.IsProd {
|
||||
_, _ = fmt.Fprintln(os.Stderr, "Gitea:", logMsg)
|
||||
}
|
||||
if userMessage != "" {
|
||||
if unicode.IsPunct(rune(userMessage[len(userMessage)-1])) {
|
||||
logMsg = userMessage + " " + logMsg
|
||||
} else {
|
||||
logMsg = userMessage + ". " + logMsg
|
||||
}
|
||||
if unicode.IsPunct(rune(userMessage[len(userMessage)-1])) {
|
||||
logMsg = userMessage + " " + logMsg
|
||||
} else {
|
||||
logMsg = userMessage + ". " + logMsg
|
||||
}
|
||||
_ = private.SSHLog(ctx, true, logMsg)
|
||||
}
|
||||
|
@ -288,10 +289,10 @@ func runServ(c *cli.Context) error {
|
|||
if allowedCommands.Contains(verb) {
|
||||
if allowedCommandsLfs.Contains(verb) {
|
||||
if !setting.LFS.StartServer {
|
||||
return fail(ctx, "Unknown git command", "LFS authentication request over SSH denied, LFS support is disabled")
|
||||
return fail(ctx, "LFS Server is not enabled", "")
|
||||
}
|
||||
if verb == verbLfsTransfer && !setting.LFS.AllowPureSSH {
|
||||
return fail(ctx, "Unknown git command", "LFS SSH transfer connection denied, pure SSH protocol is disabled")
|
||||
return fail(ctx, "LFS SSH transfer is not enabled", "")
|
||||
}
|
||||
if len(words) > 2 {
|
||||
lfsVerb = words[2]
|
||||
|
|
15
cmd/web.go
15
cmd/web.go
|
@ -12,15 +12,18 @@ import (
|
|||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
|
||||
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
"code.gitea.io/gitea/modules/gtprof"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
"code.gitea.io/gitea/modules/public"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/routers"
|
||||
"code.gitea.io/gitea/routers/install"
|
||||
|
||||
|
@ -115,6 +118,16 @@ func showWebStartupMessage(msg string) {
|
|||
log.Info("* CustomPath: %s", setting.CustomPath)
|
||||
log.Info("* ConfigFile: %s", setting.CustomConf)
|
||||
log.Info("%s", msg) // show startup message
|
||||
|
||||
if setting.CORSConfig.Enabled {
|
||||
log.Info("CORS Service Enabled")
|
||||
}
|
||||
if setting.DefaultUILocation != time.Local {
|
||||
log.Info("Default UI Location is %v", setting.DefaultUILocation.String())
|
||||
}
|
||||
if setting.MailService != nil {
|
||||
log.Info("Mail Service Enabled: RegisterEmailConfirm=%v, Service.EnableNotifyMail=%v", setting.Service.RegisterEmailConfirm, setting.Service.EnableNotifyMail)
|
||||
}
|
||||
}
|
||||
|
||||
func serveInstall(ctx *cli.Context) error {
|
||||
|
@ -208,6 +221,8 @@ func serveInstalled(ctx *cli.Context) error {
|
|||
}
|
||||
}
|
||||
|
||||
gtprof.EnableBuiltinTracer(util.Iif(setting.IsProd, 2000*time.Millisecond, 100*time.Millisecond))
|
||||
|
||||
// Set up Chi routes
|
||||
webRoutes := routers.NormalRoutes()
|
||||
err := listen(webRoutes, true)
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
)
|
||||
|
@ -54,8 +55,6 @@ func runACME(listenAddr string, m http.Handler) error {
|
|||
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 != "" {
|
||||
|
@ -65,8 +64,20 @@ func runACME(listenAddr string, m http.Handler) error {
|
|||
log.Warn("Failed to parse CA Root certificate, using default CA trust: %v", err)
|
||||
}
|
||||
}
|
||||
myACME := certmagic.NewACMEIssuer(magic, certmagic.ACMEIssuer{
|
||||
CA: setting.AcmeURL,
|
||||
// FIXME: this path is not right, it uses "AppWorkPath" incorrectly, and writes the data into "AppWorkPath/https"
|
||||
// Ideally it should migrate to AppDataPath write to "AppDataPath/https"
|
||||
// And one more thing, no idea why we should set the global default variables here
|
||||
// But it seems that the current ACME code needs these global variables to make renew work.
|
||||
// Otherwise, "renew" will use incorrect storage path
|
||||
oldDefaultACME := certmagic.DefaultACME
|
||||
certmagic.Default.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory}
|
||||
certmagic.DefaultACME = certmagic.ACMEIssuer{
|
||||
// try to use the default values provided by DefaultACME
|
||||
CA: util.IfZero(setting.AcmeURL, oldDefaultACME.CA),
|
||||
TestCA: oldDefaultACME.TestCA,
|
||||
Logger: oldDefaultACME.Logger,
|
||||
HTTPProxy: oldDefaultACME.HTTPProxy,
|
||||
|
||||
TrustedRoots: certPool,
|
||||
Email: setting.AcmeEmail,
|
||||
Agreed: setting.AcmeTOS,
|
||||
|
@ -75,8 +86,10 @@ func runACME(listenAddr string, m http.Handler) error {
|
|||
ListenHost: setting.HTTPAddr,
|
||||
AltTLSALPNPort: altTLSALPNPort,
|
||||
AltHTTPPort: altHTTPPort,
|
||||
})
|
||||
}
|
||||
|
||||
magic := certmagic.NewDefault()
|
||||
myACME := certmagic.NewACMEIssuer(magic, certmagic.DefaultACME)
|
||||
magic.Issuers = []certmagic.Issuer{myACME}
|
||||
|
||||
// this obtains certificates or renews them if necessary
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//nolint:forbidigo
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"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:
|
||||
// go run -tags 'sqlite sqlite_unlock_notify' contrib/fixtures/fixture_generation.go [fixture...]
|
||||
|
||||
var (
|
||||
generators = []struct {
|
||||
gen func(ctx context.Context) (string, error)
|
||||
name string
|
||||
}{
|
||||
{
|
||||
models.GetYamlFixturesAccess, "access",
|
||||
},
|
||||
}
|
||||
fixturesDir string
|
||||
)
|
||||
|
||||
func main() {
|
||||
pathToGiteaRoot := "."
|
||||
fixturesDir = filepath.Join(pathToGiteaRoot, "models", "fixtures")
|
||||
if err := unittest.CreateTestEngine(unittest.FixturesOptions{
|
||||
Dir: fixturesDir,
|
||||
}); err != nil {
|
||||
fmt.Printf("CreateTestEngine: %+v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := unittest.PrepareTestDatabase(); err != nil {
|
||||
fmt.Printf("PrepareTestDatabase: %+v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
ctx := context.Background()
|
||||
if len(os.Args) == 0 {
|
||||
for _, r := range os.Args {
|
||||
if err := generate(ctx, r); err != nil {
|
||||
fmt.Printf("generate '%s': %+v\n", r, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, g := range generators {
|
||||
if err := generate(ctx, g.name); err != nil {
|
||||
fmt.Printf("generate '%s': %+v\n", g.name, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generate(ctx context.Context, name string) error {
|
||||
for _, g := range generators {
|
||||
if g.name == name {
|
||||
data, err := g.gen(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path := filepath.Join(fixturesDir, name+".yml")
|
||||
if err := os.WriteFile(path, []byte(data), 0o644); err != nil {
|
||||
return fmt.Errorf("%s: %+v", path, err)
|
||||
}
|
||||
fmt.Printf("%s created.\n", path)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("generator not found")
|
||||
}
|
|
@ -78,8 +78,9 @@ RUN_USER = ; git
|
|||
;; Set the domain for the server
|
||||
;DOMAIN = localhost
|
||||
;;
|
||||
;; Overwrite the automatically generated public URL. Necessary for proxies and docker.
|
||||
;ROOT_URL = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
|
||||
;; The AppURL used by Gitea to generate absolute links, defaults to "{PROTOCOL}://{DOMAIN}:{HTTP_PORT}/".
|
||||
;; Most users should set it to the real website URL of their Gitea instance.
|
||||
;ROOT_URL =
|
||||
;;
|
||||
;; For development purpose only. It makes Gitea handle sub-path ("/sub-path/owner/repo/...") directly when debugging without a reverse proxy.
|
||||
;; DO NOT USE IT IN PRODUCTION!!!
|
||||
|
@ -103,8 +104,8 @@ RUN_USER = ; git
|
|||
;REDIRECT_OTHER_PORT = false
|
||||
;PORT_TO_REDIRECT = 80
|
||||
;;
|
||||
;; expect PROXY protocol header on connections to https redirector.
|
||||
;REDIRECTOR_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL)s
|
||||
;; expect PROXY protocol header on connections to https redirector, defaults to USE_PROXY_PROTOCOL
|
||||
;REDIRECTOR_USE_PROXY_PROTOCOL =
|
||||
;; Minimum and maximum supported TLS versions
|
||||
;SSL_MIN_VERSION=TLSv1.2
|
||||
;SSL_MAX_VERSION=
|
||||
|
@ -128,13 +129,14 @@ RUN_USER = ; git
|
|||
;; most cases you do not need to change the default value. Alter it only if
|
||||
;; your SSH server node is not the same as HTTP node. For different protocol, the default
|
||||
;; values are different. If `PROTOCOL` is `http+unix`, the default value is `http://unix/`.
|
||||
;; If `PROTOCOL` is `fcgi` or `fcgi+unix`, the default value is `%(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/`.
|
||||
;; If listen on `0.0.0.0`, the default value is `%(PROTOCOL)s://localhost:%(HTTP_PORT)s/`, Otherwise the default
|
||||
;; value is `%(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/`.
|
||||
;LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
|
||||
;; If `PROTOCOL` is `fcgi` or `fcgi+unix`, the default value is `{PROTOCOL}://{HTTP_ADDR}:{HTTP_PORT}/`.
|
||||
;; If listen on `0.0.0.0`, the default value is `{PROTOCOL}://localhost:{HTTP_PORT}/`.
|
||||
;; Otherwise the default value is `{PROTOCOL}://{HTTP_ADDR}:{HTTP_PORT}/`.
|
||||
;; Most users don't need (and shouldn't) set this value.
|
||||
;LOCAL_ROOT_URL =
|
||||
;;
|
||||
;; When making local connections pass the PROXY protocol header.
|
||||
;LOCAL_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL)s
|
||||
;; When making local connections pass the PROXY protocol header, defaults to USE_PROXY_PROTOCOL
|
||||
;LOCAL_USE_PROXY_PROTOCOL =
|
||||
;;
|
||||
;; Disable SSH feature when not available
|
||||
;DISABLE_SSH = false
|
||||
|
@ -146,13 +148,17 @@ RUN_USER = ; git
|
|||
;SSH_SERVER_USE_PROXY_PROTOCOL = false
|
||||
;;
|
||||
;; Username to use for the builtin SSH server. If blank, then it is the value of RUN_USER.
|
||||
;BUILTIN_SSH_SERVER_USER = %(RUN_USER)s
|
||||
;BUILTIN_SSH_SERVER_USER =
|
||||
;;
|
||||
;; Domain name to be exposed in clone URL
|
||||
;SSH_DOMAIN = %(DOMAIN)s
|
||||
;; Domain name to be exposed in clone URL, defaults to DOMAIN or the domain part of ROOT_URL
|
||||
;SSH_DOMAIN =
|
||||
;;
|
||||
;; SSH username displayed in clone URLs.
|
||||
;SSH_USER = %(BUILTIN_SSH_SERVER_USER)s
|
||||
;; SSH username displayed in clone URLs. It defaults to BUILTIN_SSH_SERVER_USER or RUN_USER.
|
||||
;; If it is set to "(DOER_USERNAME)", it will use current signed-in user's username.
|
||||
;; This option is only for some advanced users who have configured their SSH reverse-proxy
|
||||
;; and need to use different usernames for git SSH clone.
|
||||
;; Most users should just leave it blank.
|
||||
;SSH_USER =
|
||||
;;
|
||||
;; The network interface the builtin SSH server should listen on
|
||||
;SSH_LISTEN_HOST =
|
||||
|
@ -160,8 +166,8 @@ RUN_USER = ; git
|
|||
;; Port number to be exposed in clone URL
|
||||
;SSH_PORT = 22
|
||||
;;
|
||||
;; The port number the builtin SSH server should listen on
|
||||
;SSH_LISTEN_PORT = %(SSH_PORT)s
|
||||
;; The port number the builtin SSH server should listen on, defaults to SSH_PORT
|
||||
;SSH_LISTEN_PORT =
|
||||
;;
|
||||
;; Root path of SSH directory, default is '~/.ssh', but you have to use '/home/git/.ssh'.
|
||||
;SSH_ROOT_PATH =
|
||||
|
@ -188,7 +194,7 @@ RUN_USER = ; git
|
|||
;;
|
||||
;; For the built-in SSH server, choose the keypair to offer as the host key
|
||||
;; The private key should be at SSH_SERVER_HOST_KEY and the public SSH_SERVER_HOST_KEY.pub
|
||||
;; relative paths are made absolute relative to the %(APP_DATA_PATH)s
|
||||
;; relative paths are made absolute relative to the APP_DATA_PATH
|
||||
;SSH_SERVER_HOST_KEYS=ssh/gitea.rsa, ssh/gogs.rsa
|
||||
;;
|
||||
;; Directory to create temporary files in when testing public keys using ssh-keygen,
|
||||
|
@ -324,6 +330,10 @@ RUN_USER = ; git
|
|||
;; Maximum number of locks returned per page
|
||||
;LFS_LOCKS_PAGING_NUM = 50
|
||||
;;
|
||||
;; When clients make lfs batch requests, reject them if there are more pointers than this number
|
||||
;; zero means 'unlimited'
|
||||
;LFS_MAX_BATCH_SIZE = 0
|
||||
;;
|
||||
;; Allow graceful restarts using SIGHUP to fork
|
||||
;ALLOW_GRACEFUL_RESTARTS = true
|
||||
;;
|
||||
|
@ -578,7 +588,7 @@ ENABLED = true
|
|||
[log]
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Root path for the log files - defaults to %(GITEA_WORK_DIR)/log
|
||||
;; Root path for the log files - defaults to "{AppWorkPath}/log"
|
||||
;ROOT_PATH =
|
||||
;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -678,8 +688,8 @@ LEVEL = Info
|
|||
;; The path of git executable. If empty, Gitea searches through the PATH environment.
|
||||
;PATH =
|
||||
;;
|
||||
;; The HOME directory for Git
|
||||
;HOME_PATH = %(APP_DATA_PATH)s/home
|
||||
;; The HOME directory for Git, defaults to "{APP_DATA_PATH}/home"
|
||||
;HOME_PATH =
|
||||
;;
|
||||
;; Disables highlight of added and removed changes
|
||||
;DISABLE_DIFF_HIGHLIGHT = false
|
||||
|
@ -780,6 +790,13 @@ LEVEL = Info
|
|||
;; Please note that setting this to false will not disable OAuth Basic or Basic authentication using a token
|
||||
;ENABLE_BASIC_AUTHENTICATION = true
|
||||
;;
|
||||
;; Show the password sign-in form (for password-based login), otherwise, only show OAuth2 or passkey login methods if they are enabled.
|
||||
;; If you set it to false, maybe it also needs to set ENABLE_BASIC_AUTHENTICATION to false to completely disable password-based authentication.
|
||||
;ENABLE_PASSWORD_SIGNIN_FORM = true
|
||||
;;
|
||||
;; Allow users to sign-in with a passkey
|
||||
;ENABLE_PASSKEY_AUTHENTICATION = true
|
||||
;;
|
||||
;; More detail: https://github.com/gogits/gogs/issues/165
|
||||
;ENABLE_REVERSE_PROXY_AUTHENTICATION = false
|
||||
; Enable this to allow reverse proxy authentication for API requests, the reverse proxy is responsible for ensuring that no CSRF is possible.
|
||||
|
@ -938,8 +955,8 @@ LEVEL = Info
|
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;[repository]
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Root path for storing all repository data. By default, it is set to %(APP_DATA_PATH)s/gitea-repositories.
|
||||
;; A relative path is interpreted as _`AppWorkPath`_/%(ROOT)s
|
||||
;; Root path for storing all repository data. By default, it is set to "{APP_DATA_PATH}/gitea-repositories".
|
||||
;; A relative path is interpreted as "{AppWorkPath}/{ROOT}" (use AppWorkPath as base path).
|
||||
;ROOT =
|
||||
;;
|
||||
;; The script type this server supports. Usually this is `bash`, but some users report that only `sh` is available.
|
||||
|
@ -1003,6 +1020,14 @@ LEVEL = Info
|
|||
;; The set of allowed values and rules are the same as DEFAULT_REPO_UNITS.
|
||||
;DEFAULT_FORK_REPO_UNITS = repo.code,repo.pulls
|
||||
;;
|
||||
;; Comma separated list of default mirror repo units.
|
||||
;; The set of allowed values and rules are the same as DEFAULT_REPO_UNITS.
|
||||
;DEFAULT_MIRROR_REPO_UNITS = repo.code,repo.releases,repo.issues,repo.wiki,repo.projects,repo.packages
|
||||
;;
|
||||
;; Comma separated list of default template repo units.
|
||||
;; The set of allowed values and rules are the same as DEFAULT_REPO_UNITS.
|
||||
;DEFAULT_TEMPLATE_REPO_UNITS = repo.code,repo.releases,repo.issues,repo.pulls,repo.wiki,repo.projects,repo.packages
|
||||
;;
|
||||
;; Prefix archive files by placing them in a directory named after the repository
|
||||
;PREFIX_ARCHIVE_FILES = true
|
||||
;;
|
||||
|
@ -1024,9 +1049,13 @@ LEVEL = Info
|
|||
;; Don't allow download source archive files from UI
|
||||
;DISABLE_DOWNLOAD_SOURCE_ARCHIVES = false
|
||||
|
||||
;; Allow fork repositories without maximum number limit
|
||||
;; Allow to fork repositories without maximum number limit
|
||||
;ALLOW_FORK_WITHOUT_MAXIMUM_LIMIT = true
|
||||
|
||||
;; Allow to fork repositories into the same owner (user or organization)
|
||||
;; This feature is experimental, not fully tested, and may be changed in the future
|
||||
;ALLOW_FORK_INTO_SAME_OWNER = false
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;[repository.editor]
|
||||
|
@ -1100,6 +1129,9 @@ LEVEL = Info
|
|||
;; In default merge messages only include approvers who are official
|
||||
;DEFAULT_MERGE_MESSAGE_OFFICIAL_APPROVERS_ONLY = true
|
||||
;;
|
||||
;; In default squash-merge messages include the commit message of all commits comprising the pull request.
|
||||
;POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES = false
|
||||
;;
|
||||
;; Add co-authored-by and co-committed-by trailers if committer does not match author
|
||||
;ADD_CO_COMMITTER_TRAILERS = true
|
||||
;;
|
||||
|
@ -1262,6 +1294,9 @@ LEVEL = Info
|
|||
;; Leave it empty to allow users to select any theme from "{CustomPath}/public/assets/css/theme-*.css"
|
||||
;THEMES =
|
||||
;;
|
||||
;; The icons for file list (basic/material), this is a temporary option which will be replaced by a user setting in the future.
|
||||
;FILE_ICON_THEME = material
|
||||
;;
|
||||
;; All available reactions users can choose on issues/prs and comments.
|
||||
;; Values can be emoji alias (:smile:) or a unicode emoji.
|
||||
;; For custom reactions, add a tightly cropped square image to public/assets/img/emoji/reaction_name.png
|
||||
|
@ -1319,6 +1354,9 @@ LEVEL = Info
|
|||
;; Number of repos that are displayed on one page
|
||||
;REPO_PAGING_NUM = 15
|
||||
|
||||
;; Number of orgs that are displayed on profile page
|
||||
;ORG_PAGING_NUM = 15
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;[ui.meta]
|
||||
|
@ -1462,6 +1500,10 @@ LEVEL = Info
|
|||
;REPO_INDEXER_EXCLUDE =
|
||||
;;
|
||||
;MAX_FILE_SIZE = 1048576
|
||||
;;
|
||||
;; Bleve engine has performance problems with fuzzy search, so we limit the fuzziness to 0 by default to disable it.
|
||||
;; If you'd like to enable it, you can set it to a value between 0 and 2.
|
||||
;TYPE_BLEVE_MAX_FUZZINESS = 0
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -1479,7 +1521,8 @@ LEVEL = Info
|
|||
;TYPE = persistable-channel
|
||||
;;
|
||||
;; data-dir for storing persistable queues and level queues, individual queues will default to `queues/common` meaning the queue is shared.
|
||||
;DATADIR = queues/ ; Relative paths will be made absolute against `%(APP_DATA_PATH)s`.
|
||||
;; Relative paths will be made absolute against "APP_DATA_PATH"
|
||||
;DATADIR = queues/
|
||||
;;
|
||||
;; Default queue length before a channel queue will block
|
||||
;LENGTH = 100000
|
||||
|
@ -1727,6 +1770,9 @@ LEVEL = Info
|
|||
;;
|
||||
;; convert \r\n to \n for Sendmail
|
||||
;SENDMAIL_CONVERT_CRLF = true
|
||||
;;
|
||||
;; convert links of attached images to inline images. Only for images hosted in this gitea instance.
|
||||
;EMBED_ATTACHMENT_IMAGES = false
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -1900,7 +1946,7 @@ LEVEL = Info
|
|||
;ENABLED = true
|
||||
;;
|
||||
;; Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
|
||||
;ALLOWED_TYPES = .csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip
|
||||
;ALLOWED_TYPES = .avif,.cpuprofile,.csv,.dmp,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.json,.jsonc,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.webp,.xls,.xlsx,.zip
|
||||
;;
|
||||
;; Max size of each file. Defaults to 2048MB
|
||||
;MAX_SIZE = 2048
|
||||
|
@ -1932,6 +1978,13 @@ LEVEL = Info
|
|||
;; Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio`
|
||||
;MINIO_SECRET_ACCESS_KEY =
|
||||
;;
|
||||
;; Preferred IAM Endpoint to override Minio's default IAM Endpoint resolution only available when STORAGE_TYPE is `minio`.
|
||||
;; If not provided and STORAGE_TYPE is `minio`, will search for and derive endpoint from known environment variables
|
||||
;; (AWS_CONTAINER_AUTHORIZATION_TOKEN, AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE, AWS_CONTAINER_CREDENTIALS_RELATIVE_URI,
|
||||
;; AWS_CONTAINER_CREDENTIALS_FULL_URI, AWS_WEB_IDENTITY_TOKEN_FILE, AWS_ROLE_ARN, AWS_ROLE_SESSION_NAME, AWS_REGION),
|
||||
;; or the DefaultIAMRoleEndpoint if not provided otherwise.
|
||||
;MINIO_IAM_ENDPOINT =
|
||||
;;
|
||||
;; Minio bucket to store the attachments only available when STORAGE_TYPE is `minio`
|
||||
;MINIO_BUCKET = gitea
|
||||
;;
|
||||
|
@ -2638,6 +2691,16 @@ LEVEL = Info
|
|||
;; override the azure blob base path if storage type is azureblob
|
||||
;AZURE_BLOB_BASE_PATH = lfs/
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; settings for Gitea's LFS client (eg: mirroring an upstream lfs endpoint)
|
||||
;;
|
||||
;[lfs_client]
|
||||
;; Limit the number of pointers in each batch request to this number
|
||||
;BATCH_SIZE = 20
|
||||
;; Limit the number of concurrent upload/download operations within a batch
|
||||
;BATCH_OPERATION_CONCURRENCY = 8
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; settings for packages, will override storage setting
|
||||
|
@ -2666,6 +2729,13 @@ LEVEL = Info
|
|||
;; Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio`
|
||||
;MINIO_SECRET_ACCESS_KEY =
|
||||
;;
|
||||
;; Preferred IAM Endpoint to override Minio's default IAM Endpoint resolution only available when STORAGE_TYPE is `minio`.
|
||||
;; If not provided and STORAGE_TYPE is `minio`, will search for and derive endpoint from known environment variables
|
||||
;; (AWS_CONTAINER_AUTHORIZATION_TOKEN, AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE, AWS_CONTAINER_CREDENTIALS_RELATIVE_URI,
|
||||
;; AWS_CONTAINER_CREDENTIALS_FULL_URI, AWS_WEB_IDENTITY_TOKEN_FILE, AWS_ROLE_ARN, AWS_ROLE_SESSION_NAME, AWS_REGION),
|
||||
;; or the DefaultIAMRoleEndpoint if not provided otherwise.
|
||||
;MINIO_IAM_ENDPOINT =
|
||||
;;
|
||||
;; Minio bucket to store the attachments only available when STORAGE_TYPE is `minio`
|
||||
;MINIO_BUCKET = gitea
|
||||
;;
|
||||
|
|
|
@ -37,5 +37,5 @@ done
|
|||
if [ $# -gt 0 ]; then
|
||||
exec "$@"
|
||||
else
|
||||
exec /bin/s6-svscan /etc/s6
|
||||
exec /usr/bin/s6-svscan /etc/s6
|
||||
fi
|
||||
|
|
12
flake.lock
12
flake.lock
|
@ -5,11 +5,11 @@
|
|||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -20,11 +20,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1720542800,
|
||||
"narHash": "sha256-ZgnNHuKV6h2+fQ5LuqnUaqZey1Lqqt5dTUAiAnqH0QQ=",
|
||||
"lastModified": 1739214665,
|
||||
"narHash": "sha256-26L8VAu3/1YRxS8MHgBOyOM8xALdo6N0I04PgorE7UM=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "feb2849fdeb70028c70d73b848214b00d324a497",
|
||||
"rev": "64e75cd44acf21c7933d61d7721e812eac1b5a0a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -22,17 +22,21 @@
|
|||
gzip
|
||||
|
||||
# frontend
|
||||
nodejs_20
|
||||
nodejs_22
|
||||
|
||||
# linting
|
||||
python312
|
||||
poetry
|
||||
|
||||
# backend
|
||||
go_1_22
|
||||
go_1_24
|
||||
gofumpt
|
||||
sqlite
|
||||
];
|
||||
shellHook = ''
|
||||
export GO="${pkgs.go_1_24}/bin/go"
|
||||
export GOROOT="${pkgs.go_1_24}/share/go"
|
||||
'';
|
||||
};
|
||||
}
|
||||
);
|
||||
|
|
248
go.mod
248
go.mod
|
@ -1,6 +1,6 @@
|
|||
module code.gitea.io/gitea
|
||||
|
||||
go 1.23
|
||||
go 1.24
|
||||
|
||||
// rfc5280 said: "The serial number is an integer assigned by the CA to each certificate."
|
||||
// But some CAs use negative serial number, just relax the check. related:
|
||||
|
@ -8,11 +8,11 @@ go 1.23
|
|||
godebug x509negativeserial=1
|
||||
|
||||
require (
|
||||
code.gitea.io/actions-proto-go v0.4.0
|
||||
code.gitea.io/actions-proto-go v0.4.1
|
||||
code.gitea.io/gitea-vet v0.2.3
|
||||
code.gitea.io/sdk/gitea v0.17.1
|
||||
code.gitea.io/sdk/gitea v0.20.0
|
||||
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
|
||||
connectrpc.com/connect v1.15.0
|
||||
connectrpc.com/connect v1.18.1
|
||||
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed
|
||||
gitea.com/go-chi/cache v0.2.1
|
||||
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098
|
||||
|
@ -20,21 +20,20 @@ require (
|
|||
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
||||
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
|
||||
github.com/42wim/httpsig v1.2.2
|
||||
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2
|
||||
github.com/42wim/sshsig v0.0.0-20240818000253-e3a6333df815
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
|
||||
github.com/ProtonMail/go-crypto v1.0.0
|
||||
github.com/PuerkitoBio/goquery v1.9.2
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2
|
||||
github.com/alecthomas/chroma/v2 v2.14.0
|
||||
github.com/aws/aws-sdk-go v1.43.21
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.30
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.25.1
|
||||
github.com/ProtonMail/go-crypto v1.1.6
|
||||
github.com/PuerkitoBio/goquery v1.10.2
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3
|
||||
github.com/alecthomas/chroma/v2 v2.15.0
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.60
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.27.16
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||
github.com/blevesearch/bleve/v2 v2.4.2
|
||||
github.com/buildkite/terminal-to-html/v3 v3.12.1
|
||||
github.com/caddyserver/certmagic v0.21.3
|
||||
github.com/buildkite/terminal-to-html/v3 v3.16.6
|
||||
github.com/caddyserver/certmagic v0.21.7
|
||||
github.com/charmbracelet/git-lfs-transfer v0.2.0
|
||||
github.com/chi-middleware/proxy v1.1.1
|
||||
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21
|
||||
|
@ -46,53 +45,50 @@ require (
|
|||
github.com/emersion/go-imap v1.2.1
|
||||
github.com/emirpasic/gods v1.18.1
|
||||
github.com/ethantkoenig/rupture v1.0.1
|
||||
github.com/felixge/fgprof v0.9.4
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/gliderlabs/ssh v0.3.7
|
||||
github.com/go-ap/activitypub v0.0.0-20240408091739-ba76b44c2594
|
||||
github.com/felixge/fgprof v0.9.5
|
||||
github.com/fsnotify/fsnotify v1.8.0
|
||||
github.com/gliderlabs/ssh v0.3.8
|
||||
github.com/go-ap/activitypub v0.0.0-20250212090640-aeb6499ba581
|
||||
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
|
||||
github.com/go-chi/chi/v5 v5.0.13
|
||||
github.com/go-chi/chi/v5 v5.2.1
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-co-op/gocron v1.37.0
|
||||
github.com/go-enry/go-enry/v2 v2.9.1
|
||||
github.com/go-git/go-billy/v5 v5.5.0
|
||||
github.com/go-git/go-git/v5 v5.12.0
|
||||
github.com/go-ldap/ldap/v3 v3.4.6
|
||||
github.com/go-enry/go-enry/v2 v2.9.2
|
||||
github.com/go-git/go-billy/v5 v5.6.2
|
||||
github.com/go-git/go-git/v5 v5.13.2
|
||||
github.com/go-ldap/ldap/v3 v3.4.10
|
||||
github.com/go-redsync/redsync/v4 v4.13.0
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
github.com/go-sql-driver/mysql v1.9.0
|
||||
github.com/go-swagger/go-swagger v0.31.0
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.11.0
|
||||
github.com/go-webauthn/webauthn v0.10.2
|
||||
github.com/go-webauthn/webauthn v0.11.2
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
github.com/google/go-github/v61 v61.0.0
|
||||
github.com/google/licenseclassifier/v2 v2.0.0
|
||||
github.com/google/pprof v0.0.0-20240618054019-d3b898a103f8
|
||||
github.com/google/pprof v0.0.0-20250208200701-d0013a598941
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/feeds v1.2.0
|
||||
github.com/gorilla/sessions v1.3.0
|
||||
github.com/h2non/gock v1.2.0
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
github.com/hashicorp/go-version v1.7.0
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/huandu/xstrings v1.5.0
|
||||
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
|
||||
github.com/jhillyerd/enmime v1.2.0
|
||||
github.com/jhillyerd/enmime v1.3.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
|
||||
github.com/klauspost/compress v1.17.9
|
||||
github.com/klauspost/cpuid/v2 v2.2.8
|
||||
github.com/klauspost/compress v1.18.0
|
||||
github.com/klauspost/cpuid/v2 v2.2.9
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/markbates/goth v1.80.0
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mattn/go-sqlite3 v1.14.22
|
||||
github.com/meilisearch/meilisearch-go v0.26.3
|
||||
github.com/mattn/go-sqlite3 v1.14.24
|
||||
github.com/meilisearch/meilisearch-go v0.29.1-0.20241106140435-0bf60fad690a
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/microcosm-cc/bluemonday v1.0.26
|
||||
github.com/microsoft/go-mssqldb v1.7.2
|
||||
github.com/minio/minio-go/v7 v7.0.77
|
||||
github.com/microcosm-cc/bluemonday v1.0.27
|
||||
github.com/microsoft/go-mssqldb v1.8.0
|
||||
github.com/minio/minio-go/v7 v7.0.87
|
||||
github.com/msteinert/pam v1.2.0
|
||||
github.com/nektos/act v0.2.63
|
||||
github.com/niklasfasching/go-org v1.7.0
|
||||
|
@ -101,69 +97,68 @@ require (
|
|||
github.com/opencontainers/image-spec v1.1.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/prometheus/client_golang v1.19.1
|
||||
github.com/prometheus/client_golang v1.21.0
|
||||
github.com/quasoft/websspi v1.1.2
|
||||
github.com/redis/go-redis/v9 v9.6.0
|
||||
github.com/redis/go-redis/v9 v9.7.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
|
||||
github.com/sassoftware/go-rpmutils v0.4.0
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
|
||||
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/tstranex/u2f v1.0.0
|
||||
github.com/ulikunitz/xz v0.5.12
|
||||
github.com/urfave/cli/v2 v2.27.2
|
||||
github.com/xanzy/go-gitlab v0.105.0
|
||||
github.com/urfave/cli/v2 v2.27.5
|
||||
github.com/wneessen/go-mail v0.6.2
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
github.com/yohcop/openid-go v1.0.1
|
||||
github.com/yuin/goldmark v1.7.2
|
||||
github.com/yuin/goldmark v1.7.8
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||
github.com/yuin/goldmark-meta v1.1.0
|
||||
golang.org/x/crypto v0.26.0
|
||||
golang.org/x/image v0.18.0
|
||||
golang.org/x/net v0.28.0
|
||||
golang.org/x/oauth2 v0.21.0
|
||||
golang.org/x/sys v0.24.0
|
||||
golang.org/x/text v0.17.0
|
||||
golang.org/x/tools v0.24.0
|
||||
google.golang.org/grpc v1.62.1
|
||||
google.golang.org/protobuf v1.34.2
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gitlab.com/gitlab-org/api/client-go v0.123.0
|
||||
golang.org/x/crypto v0.35.0
|
||||
golang.org/x/image v0.24.0
|
||||
golang.org/x/net v0.36.0
|
||||
golang.org/x/oauth2 v0.27.0
|
||||
golang.org/x/sync v0.11.0
|
||||
golang.org/x/sys v0.30.0
|
||||
golang.org/x/text v0.22.0
|
||||
golang.org/x/tools v0.30.0
|
||||
google.golang.org/grpc v1.70.0
|
||||
google.golang.org/protobuf v1.36.5
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
mvdan.cc/xurls/v2 v2.5.0
|
||||
mvdan.cc/xurls/v2 v2.6.0
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
|
||||
xorm.io/builder v0.3.13
|
||||
xorm.io/xorm v1.3.9
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 // indirect
|
||||
github.com/ClickHouse/ch-go v0.61.5 // indirect
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.25.0 // indirect
|
||||
github.com/DataDog/zstd v1.5.5 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
||||
github.com/DataDog/zstd v1.5.6 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.1 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/RoaringBitmap/roaring v1.9.4 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.2 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 // indirect
|
||||
github.com/aws/smithy-go v1.20.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.33 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.33 // indirect
|
||||
github.com/aws/smithy-go v1.22.3 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.13.0 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.1.10 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.20.0 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.1.12 // indirect
|
||||
github.com/blevesearch/geo v0.1.20 // indirect
|
||||
github.com/blevesearch/go-faiss v1.0.20 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||
|
@ -180,31 +175,30 @@ require (
|
|||
github.com/blevesearch/zapx/v14 v14.3.10 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.3.13 // indirect
|
||||
github.com/blevesearch/zapx/v16 v16.1.5 // indirect
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect
|
||||
github.com/boombuler/barcode v1.0.2 // indirect
|
||||
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudflare/circl v1.3.9 // indirect
|
||||
github.com/cloudflare/circl v1.6.0 // indirect
|
||||
github.com/couchbase/go-couchbase v0.1.1 // indirect
|
||||
github.com/couchbase/gomemcached v0.3.1 // indirect
|
||||
github.com/couchbase/gomemcached v0.3.2 // indirect
|
||||
github.com/couchbase/goutils v0.1.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.5 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dlclark/regexp2 v1.11.0 // indirect
|
||||
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
|
||||
github.com/fatih/color v1.17.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect
|
||||
github.com/go-ap/errors v0.0.0-20240304112515-6077fa9c17b0 // indirect
|
||||
github.com/go-ap/errors v0.0.0-20250124135319-3da8adefd4a9 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
|
||||
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
||||
github.com/go-faster/city v1.0.1 // indirect
|
||||
github.com/go-faster/errors v0.7.1 // indirect
|
||||
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
|
@ -219,44 +213,44 @@ require (
|
|||
github.com/go-openapi/strfmt v0.23.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-openapi/validate v0.24.0 // indirect
|
||||
github.com/go-webauthn/x v0.1.9 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/go-webauthn/x v0.1.16 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/golang/geo v0.0.0-20230421003525-6adc56603217 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/go-tpm v0.9.0 // indirect
|
||||
github.com/google/go-tpm v0.9.3 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/gorilla/handlers v1.5.2 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jessevdk/go-flags v1.5.0 // indirect
|
||||
github.com/jessevdk/go-flags v1.6.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/libdns/libdns v0.2.2 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/libdns/libdns v0.2.3 // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/markbates/going v1.0.3 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mholt/acmez/v2 v2.0.1 // indirect
|
||||
github.com/miekg/dns v1.1.61 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
github.com/mholt/acmez/v3 v3.0.1 // indirect
|
||||
github.com/miekg/dns v1.1.63 // indirect
|
||||
github.com/minio/crc64nvme v1.0.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
|
@ -270,38 +264,33 @@ require (
|
|||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/gomega v1.33.1 // indirect
|
||||
github.com/paulmach/orb v0.11.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/rhysd/actionlint v1.7.1 // indirect
|
||||
github.com/rhysd/actionlint v1.7.7 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/segmentio/asm v1.2.0 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.2.2 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.18.2 // indirect
|
||||
github.com/spf13/afero v1.12.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/spf13/viper v1.19.0 // indirect
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/toqueteos/webbrowser v1.2.0 // indirect
|
||||
github.com/unknwon/com v1.0.1 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.55.0 // indirect
|
||||
github.com/valyala/fastjson v1.6.4 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
|
@ -309,20 +298,18 @@ require (
|
|||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
go.etcd.io/bbolt v1.3.10 // indirect
|
||||
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||
go.opentelemetry.io/otel v1.27.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.27.0 // indirect
|
||||
github.com/zeebo/assert v1.3.0 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
go.etcd.io/bbolt v1.4.0 // indirect
|
||||
go.mongodb.org/mongo-driver v1.17.2 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f // indirect
|
||||
golang.org/x/mod v0.20.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect
|
||||
golang.org/x/mod v0.23.0 // indirect
|
||||
golang.org/x/time v0.10.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250219182151-9fdb1cabc7b2 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
@ -331,8 +318,9 @@ replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
|
|||
|
||||
replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0
|
||||
|
||||
replace github.com/nektos/act => gitea.com/gitea/act v0.261.3
|
||||
replace github.com/nektos/act => gitea.com/gitea/act v0.261.4
|
||||
|
||||
// TODO: the only difference is in `PutObject`: the fork doesn't use `NewVerifyingReader(r, sha256.New(), oid, expectedSize)`, need to figure out why
|
||||
replace github.com/charmbracelet/git-lfs-transfer => gitea.com/gitea/git-lfs-transfer v0.2.0
|
||||
|
||||
// TODO: This could be removed after https://github.com/mholt/archiver/pull/396 merged
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build windows
|
||||
|
||||
package main
|
||||
|
||||
// Golang has the ability to load OS's timezone data from most UNIX systems (https://github.com/golang/go/blob/master/src/time/zoneinfo_unix.go)
|
||||
// Even if the timezone data is missing, users could install the related packages to get it.
|
||||
// But on Windows, although `zoneinfo_windows.go` tries to load the timezone data from Windows registry,
|
||||
// some users still suffer from the issue that the timezone data is missing: https://github.com/go-gitea/gitea/issues/33235
|
||||
// So we import the tzdata package to make sure the timezone data is included in the binary.
|
||||
//
|
||||
// For non-Windows package builders, they could still use the "TAGS=timetzdata" to include the tzdata package in the binary.
|
||||
// If we decided to add the tzdata for other platforms, modify the "go:build" directive above.
|
||||
import _ "time/tzdata"
|
|
@ -48,7 +48,7 @@ type ActionArtifact struct {
|
|||
ContentEncoding string // The content encoding of the artifact
|
||||
ArtifactPath string `xorm:"index unique(runid_name_path)"` // The path to the artifact when runner uploads it
|
||||
ArtifactName string `xorm:"index unique(runid_name_path)"` // The name of the artifact when runner uploads it
|
||||
Status int64 `xorm:"index"` // The status of the artifact, uploading, expired or need-delete
|
||||
Status ArtifactStatus `xorm:"index"` // The status of the artifact, uploading, expired or need-delete
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated index"`
|
||||
ExpiredUnix timeutil.TimeStamp `xorm:"index"` // The time when the artifact will be expired
|
||||
|
@ -68,7 +68,7 @@ func CreateArtifact(ctx context.Context, t *ActionTask, artifactName, artifactPa
|
|||
RepoID: t.RepoID,
|
||||
OwnerID: t.OwnerID,
|
||||
CommitSHA: t.CommitSHA,
|
||||
Status: int64(ArtifactStatusUploadPending),
|
||||
Status: ArtifactStatusUploadPending,
|
||||
ExpiredUnix: timeutil.TimeStamp(time.Now().Unix() + timeutil.Day*expiredDays),
|
||||
}
|
||||
if _, err := db.GetEngine(ctx).Insert(artifact); err != nil {
|
||||
|
@ -108,12 +108,19 @@ func UpdateArtifactByID(ctx context.Context, id int64, art *ActionArtifact) erro
|
|||
|
||||
type FindArtifactsOptions struct {
|
||||
db.ListOptions
|
||||
RepoID int64
|
||||
RunID int64
|
||||
ArtifactName string
|
||||
Status int
|
||||
RepoID int64
|
||||
RunID int64
|
||||
ArtifactName string
|
||||
Status int
|
||||
FinalizedArtifactsV4 bool
|
||||
}
|
||||
|
||||
func (opts FindArtifactsOptions) ToOrders() string {
|
||||
return "id"
|
||||
}
|
||||
|
||||
var _ db.FindOptionsOrder = (*FindArtifactsOptions)(nil)
|
||||
|
||||
func (opts FindArtifactsOptions) ToConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if opts.RepoID > 0 {
|
||||
|
@ -128,11 +135,15 @@ func (opts FindArtifactsOptions) ToConds() builder.Cond {
|
|||
if opts.Status > 0 {
|
||||
cond = cond.And(builder.Eq{"status": opts.Status})
|
||||
}
|
||||
if opts.FinalizedArtifactsV4 {
|
||||
cond = cond.And(builder.Eq{"status": ArtifactStatusUploadConfirmed}.Or(builder.Eq{"status": ArtifactStatusExpired}))
|
||||
cond = cond.And(builder.Eq{"content_encoding": "application/zip"})
|
||||
}
|
||||
|
||||
return cond
|
||||
}
|
||||
|
||||
// ActionArtifactMeta is the meta data of an artifact
|
||||
// ActionArtifactMeta is the meta-data of an artifact
|
||||
type ActionArtifactMeta struct {
|
||||
ArtifactName string
|
||||
FileSize int64
|
||||
|
@ -166,18 +177,18 @@ func ListPendingDeleteArtifacts(ctx context.Context, limit int) ([]*ActionArtifa
|
|||
|
||||
// SetArtifactExpired sets an artifact to expired
|
||||
func SetArtifactExpired(ctx context.Context, artifactID int64) error {
|
||||
_, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusExpired)})
|
||||
_, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusExpired})
|
||||
return err
|
||||
}
|
||||
|
||||
// SetArtifactNeedDelete sets an artifact to need-delete, cron job will delete it
|
||||
func SetArtifactNeedDelete(ctx context.Context, runID int64, name string) error {
|
||||
_, err := db.GetEngine(ctx).Where("run_id=? AND artifact_name=? AND status = ?", runID, name, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusPendingDeletion)})
|
||||
_, err := db.GetEngine(ctx).Where("run_id=? AND artifact_name=? AND status = ?", runID, name, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusPendingDeletion})
|
||||
return err
|
||||
}
|
||||
|
||||
// SetArtifactDeleted sets an artifact to deleted
|
||||
func SetArtifactDeleted(ctx context.Context, artifactID int64) error {
|
||||
_, err := db.GetEngine(ctx).ID(artifactID).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusDeleted)})
|
||||
_, err := db.GetEngine(ctx).ID(artifactID).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusDeleted})
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ type ActionRun struct {
|
|||
TriggerUser *user_model.User `xorm:"-"`
|
||||
ScheduleID int64
|
||||
Ref string `xorm:"index"` // the commit/tag/… that caused the run
|
||||
IsRefDeleted bool `xorm:"-"`
|
||||
CommitSHA string
|
||||
IsForkPullRequest bool // If this is triggered by a PR from a forked repository or an untrusted user, we need to check if it is approved and limit permissions when running the workflow.
|
||||
NeedApproval bool // may need approval if it's a fork pull request
|
||||
|
@ -87,7 +88,7 @@ func (run *ActionRun) RefLink() string {
|
|||
if refName.IsPull() {
|
||||
return run.Repo.Link() + "/pulls/" + refName.ShortName()
|
||||
}
|
||||
return git.RefURL(run.Repo.Link(), run.Ref)
|
||||
return run.Repo.Link() + "/src/" + refName.RefWebLinkPath()
|
||||
}
|
||||
|
||||
// PrettyRef return #id for pull ref or ShortName for others
|
||||
|
@ -153,7 +154,7 @@ func (run *ActionRun) GetPushEventPayload() (*api.PushPayload, error) {
|
|||
}
|
||||
|
||||
func (run *ActionRun) GetPullRequestEventPayload() (*api.PullRequestPayload, error) {
|
||||
if run.Event == webhook_module.HookEventPullRequest || run.Event == webhook_module.HookEventPullRequestSync {
|
||||
if run.Event.IsPullRequest() {
|
||||
var payload api.PullRequestPayload
|
||||
if err := json.Unmarshal([]byte(run.EventPayload), &payload); err != nil {
|
||||
return nil, err
|
||||
|
@ -193,7 +194,7 @@ func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) err
|
|||
|
||||
// CancelPreviousJobs cancels all previous jobs of the same repository, reference, workflow, and event.
|
||||
// It's useful when a new run is triggered, and all previous runs needn't be continued anymore.
|
||||
func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID string, event webhook_module.HookEventType) error {
|
||||
func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID string, event webhook_module.HookEventType) ([]*ActionRunJob, error) {
|
||||
// Find all runs in the specified repository, reference, and workflow with non-final status
|
||||
runs, total, err := db.FindAndCount[ActionRun](ctx, FindRunOptions{
|
||||
RepoID: repoID,
|
||||
|
@ -203,14 +204,16 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
|
|||
Status: []Status{StatusRunning, StatusWaiting, StatusBlocked},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If there are no runs found, there's no need to proceed with cancellation, so return nil.
|
||||
if total == 0 {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
cancelledJobs := make([]*ActionRunJob, 0, total)
|
||||
|
||||
// Iterate over each found run and cancel its associated jobs.
|
||||
for _, run := range runs {
|
||||
// Find all jobs associated with the current run.
|
||||
|
@ -218,7 +221,7 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
|
|||
RunID: run.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return cancelledJobs, err
|
||||
}
|
||||
|
||||
// Iterate over each job and attempt to cancel it.
|
||||
|
@ -237,30 +240,33 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
|
|||
// Update the job's status and stopped time in the database.
|
||||
n, err := UpdateRunJob(ctx, job, builder.Eq{"task_id": 0}, "status", "stopped")
|
||||
if err != nil {
|
||||
return err
|
||||
return cancelledJobs, err
|
||||
}
|
||||
|
||||
// If the update affected 0 rows, it means the job has changed in the meantime, so we need to try again.
|
||||
if n == 0 {
|
||||
return fmt.Errorf("job has changed, try again")
|
||||
return cancelledJobs, fmt.Errorf("job has changed, try again")
|
||||
}
|
||||
|
||||
cancelledJobs = append(cancelledJobs, job)
|
||||
// Continue with the next job.
|
||||
continue
|
||||
}
|
||||
|
||||
// If the job has an associated task, try to stop the task, effectively cancelling the job.
|
||||
if err := StopTask(ctx, job.TaskID, StatusCancelled); err != nil {
|
||||
return err
|
||||
return cancelledJobs, err
|
||||
}
|
||||
cancelledJobs = append(cancelledJobs, job)
|
||||
}
|
||||
}
|
||||
|
||||
// Return nil to indicate successful cancellation of all running and waiting jobs.
|
||||
return nil
|
||||
return cancelledJobs, nil
|
||||
}
|
||||
|
||||
// InsertRun inserts a run
|
||||
// The title will be cut off at 255 characters if it's longer than 255 characters.
|
||||
func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWorkflow) error {
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
|
@ -273,6 +279,7 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
|
|||
return err
|
||||
}
|
||||
run.Index = index
|
||||
run.Title = util.EllipsisDisplayString(run.Title, 255)
|
||||
|
||||
if err := db.Insert(ctx, run); err != nil {
|
||||
return err
|
||||
|
@ -305,7 +312,7 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
|
|||
} else {
|
||||
hasWaiting = true
|
||||
}
|
||||
job.Name, _ = util.SplitStringAtByteN(job.Name, 255)
|
||||
job.Name = util.EllipsisDisplayString(job.Name, 255)
|
||||
runJobs = append(runJobs, &ActionRunJob{
|
||||
RunID: run.ID,
|
||||
RepoID: run.RepoID,
|
||||
|
@ -399,6 +406,7 @@ func UpdateRun(ctx context.Context, run *ActionRun, cols ...string) error {
|
|||
if len(cols) > 0 {
|
||||
sess.Cols(cols...)
|
||||
}
|
||||
run.Title = util.EllipsisDisplayString(run.Title, 255)
|
||||
affected, err := sess.Update(run)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -137,7 +137,7 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
|
|||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
run.Status = aggregateJobStatus(jobs)
|
||||
run.Status = AggregateJobStatus(jobs)
|
||||
if run.Started.IsZero() && run.Status.IsRunning() {
|
||||
run.Started = timeutil.TimeStampNow()
|
||||
}
|
||||
|
@ -152,29 +152,35 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
|
|||
return affected, nil
|
||||
}
|
||||
|
||||
func aggregateJobStatus(jobs []*ActionRunJob) Status {
|
||||
allDone := true
|
||||
allWaiting := true
|
||||
hasFailure := false
|
||||
func AggregateJobStatus(jobs []*ActionRunJob) Status {
|
||||
allSuccessOrSkipped := len(jobs) != 0
|
||||
allSkipped := len(jobs) != 0
|
||||
var hasFailure, hasCancelled, hasWaiting, hasRunning, hasBlocked bool
|
||||
for _, job := range jobs {
|
||||
if !job.Status.IsDone() {
|
||||
allDone = false
|
||||
}
|
||||
if job.Status != StatusWaiting && !job.Status.IsDone() {
|
||||
allWaiting = false
|
||||
}
|
||||
if job.Status == StatusFailure || job.Status == StatusCancelled {
|
||||
hasFailure = true
|
||||
}
|
||||
allSuccessOrSkipped = allSuccessOrSkipped && (job.Status == StatusSuccess || job.Status == StatusSkipped)
|
||||
allSkipped = allSkipped && job.Status == StatusSkipped
|
||||
hasFailure = hasFailure || job.Status == StatusFailure
|
||||
hasCancelled = hasCancelled || job.Status == StatusCancelled
|
||||
hasWaiting = hasWaiting || job.Status == StatusWaiting
|
||||
hasRunning = hasRunning || job.Status == StatusRunning
|
||||
hasBlocked = hasBlocked || job.Status == StatusBlocked
|
||||
}
|
||||
if allDone {
|
||||
if hasFailure {
|
||||
return StatusFailure
|
||||
}
|
||||
switch {
|
||||
case allSkipped:
|
||||
return StatusSkipped
|
||||
case allSuccessOrSkipped:
|
||||
return StatusSuccess
|
||||
}
|
||||
if allWaiting {
|
||||
case hasCancelled:
|
||||
return StatusCancelled
|
||||
case hasFailure:
|
||||
return StatusFailure
|
||||
case hasRunning:
|
||||
return StatusRunning
|
||||
case hasWaiting:
|
||||
return StatusWaiting
|
||||
case hasBlocked:
|
||||
return StatusBlocked
|
||||
default:
|
||||
return StatusUnknown // it shouldn't happen
|
||||
}
|
||||
return StatusRunning
|
||||
}
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAggregateJobStatus(t *testing.T) {
|
||||
testStatuses := func(expected Status, statuses []Status) {
|
||||
t.Helper()
|
||||
var jobs []*ActionRunJob
|
||||
for _, v := range statuses {
|
||||
jobs = append(jobs, &ActionRunJob{Status: v})
|
||||
}
|
||||
actual := AggregateJobStatus(jobs)
|
||||
if !assert.Equal(t, expected, actual) {
|
||||
var statusStrings []string
|
||||
for _, s := range statuses {
|
||||
statusStrings = append(statusStrings, s.String())
|
||||
}
|
||||
t.Errorf("AggregateJobStatus(%v) = %v, want %v", statusStrings, statusNames[actual], statusNames[expected])
|
||||
}
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
statuses []Status
|
||||
expected Status
|
||||
}{
|
||||
// unknown cases, maybe it shouldn't happen in real world
|
||||
{[]Status{}, StatusUnknown},
|
||||
{[]Status{StatusUnknown, StatusSuccess}, StatusUnknown},
|
||||
{[]Status{StatusUnknown, StatusSkipped}, StatusUnknown},
|
||||
{[]Status{StatusUnknown, StatusFailure}, StatusFailure},
|
||||
{[]Status{StatusUnknown, StatusCancelled}, StatusCancelled},
|
||||
{[]Status{StatusUnknown, StatusWaiting}, StatusWaiting},
|
||||
{[]Status{StatusUnknown, StatusRunning}, StatusRunning},
|
||||
{[]Status{StatusUnknown, StatusBlocked}, StatusBlocked},
|
||||
|
||||
// success with other status
|
||||
{[]Status{StatusSuccess}, StatusSuccess},
|
||||
{[]Status{StatusSuccess, StatusSkipped}, StatusSuccess}, // skipped doesn't affect success
|
||||
{[]Status{StatusSuccess, StatusFailure}, StatusFailure},
|
||||
{[]Status{StatusSuccess, StatusCancelled}, StatusCancelled},
|
||||
{[]Status{StatusSuccess, StatusWaiting}, StatusWaiting},
|
||||
{[]Status{StatusSuccess, StatusRunning}, StatusRunning},
|
||||
{[]Status{StatusSuccess, StatusBlocked}, StatusBlocked},
|
||||
|
||||
// any cancelled, then cancelled
|
||||
{[]Status{StatusCancelled}, StatusCancelled},
|
||||
{[]Status{StatusCancelled, StatusSuccess}, StatusCancelled},
|
||||
{[]Status{StatusCancelled, StatusSkipped}, StatusCancelled},
|
||||
{[]Status{StatusCancelled, StatusFailure}, StatusCancelled},
|
||||
{[]Status{StatusCancelled, StatusWaiting}, StatusCancelled},
|
||||
{[]Status{StatusCancelled, StatusRunning}, StatusCancelled},
|
||||
{[]Status{StatusCancelled, StatusBlocked}, StatusCancelled},
|
||||
|
||||
// failure with other status, fail fast
|
||||
// Should "running" win? Maybe no: old code does make "running" win, but GitHub does fail fast.
|
||||
{[]Status{StatusFailure}, StatusFailure},
|
||||
{[]Status{StatusFailure, StatusSuccess}, StatusFailure},
|
||||
{[]Status{StatusFailure, StatusSkipped}, StatusFailure},
|
||||
{[]Status{StatusFailure, StatusCancelled}, StatusCancelled},
|
||||
{[]Status{StatusFailure, StatusWaiting}, StatusFailure},
|
||||
{[]Status{StatusFailure, StatusRunning}, StatusFailure},
|
||||
{[]Status{StatusFailure, StatusBlocked}, StatusFailure},
|
||||
|
||||
// skipped with other status
|
||||
// "all skipped" is also considered as "mergeable" by "services/actions.toCommitStatus", the same as GitHub
|
||||
{[]Status{StatusSkipped}, StatusSkipped},
|
||||
{[]Status{StatusSkipped, StatusSuccess}, StatusSuccess},
|
||||
{[]Status{StatusSkipped, StatusFailure}, StatusFailure},
|
||||
{[]Status{StatusSkipped, StatusCancelled}, StatusCancelled},
|
||||
{[]Status{StatusSkipped, StatusWaiting}, StatusWaiting},
|
||||
{[]Status{StatusSkipped, StatusRunning}, StatusRunning},
|
||||
{[]Status{StatusSkipped, StatusBlocked}, StatusBlocked},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
testStatuses(c.expected, c.statuses)
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import (
|
|||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||
|
||||
"xorm.io/builder"
|
||||
|
@ -112,14 +113,14 @@ type StatusInfo struct {
|
|||
}
|
||||
|
||||
// GetStatusInfoList returns a slice of StatusInfo
|
||||
func GetStatusInfoList(ctx context.Context) []StatusInfo {
|
||||
func GetStatusInfoList(ctx context.Context, lang translation.Locale) []StatusInfo {
|
||||
// same as those in aggregateJobStatus
|
||||
allStatus := []Status{StatusSuccess, StatusFailure, StatusWaiting, StatusRunning}
|
||||
statusInfoList := make([]StatusInfo, 0, 4)
|
||||
for _, s := range allStatus {
|
||||
statusInfoList = append(statusInfoList, StatusInfo{
|
||||
Status: int(s),
|
||||
DisplayedStatus: s.String(),
|
||||
DisplayedStatus: s.LocaleString(lang),
|
||||
})
|
||||
}
|
||||
return statusInfoList
|
||||
|
|
|
@ -167,6 +167,7 @@ func init() {
|
|||
|
||||
type FindRunnerOptions struct {
|
||||
db.ListOptions
|
||||
IDs []int64
|
||||
RepoID int64
|
||||
OwnerID int64 // it will be ignored if RepoID is set
|
||||
Sort string
|
||||
|
@ -178,6 +179,14 @@ type FindRunnerOptions struct {
|
|||
func (opts FindRunnerOptions) ToConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
|
||||
if len(opts.IDs) > 0 {
|
||||
if len(opts.IDs) == 1 {
|
||||
cond = cond.And(builder.Eq{"id": opts.IDs[0]})
|
||||
} else {
|
||||
cond = cond.And(builder.In("id", opts.IDs))
|
||||
}
|
||||
}
|
||||
|
||||
if opts.RepoID > 0 {
|
||||
c := builder.NewCond().And(builder.Eq{"repo_id": opts.RepoID})
|
||||
if opts.WithAvailable {
|
||||
|
@ -252,6 +261,7 @@ func GetRunnerByID(ctx context.Context, id int64) (*ActionRunner, error) {
|
|||
// UpdateRunner updates runner's information.
|
||||
func UpdateRunner(ctx context.Context, r *ActionRunner, cols ...string) error {
|
||||
e := db.GetEngine(ctx)
|
||||
r.Name = util.EllipsisDisplayString(r.Name, 255)
|
||||
var err error
|
||||
if len(cols) == 0 {
|
||||
_, err = e.ID(r.ID).AllCols().Update(r)
|
||||
|
@ -278,6 +288,7 @@ func CreateRunner(ctx context.Context, t *ActionRunner) error {
|
|||
// Remove OwnerID to avoid confusion; it's not worth returning an error here.
|
||||
t.OwnerID = 0
|
||||
}
|
||||
t.Name = util.EllipsisDisplayString(t.Name, 255)
|
||||
return db.Insert(ctx, t)
|
||||
}
|
||||
|
||||
|
@ -326,3 +337,17 @@ func FixRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) {
|
|||
}
|
||||
return res.RowsAffected()
|
||||
}
|
||||
|
||||
func CountWrongRepoLevelRunners(ctx context.Context) (int64, error) {
|
||||
var result int64
|
||||
_, err := db.GetEngine(ctx).SQL("SELECT count(`id`) FROM `action_runner` WHERE `repo_id` > 0 AND `owner_id` > 0").Get(&result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func UpdateWrongRepoLevelRunners(ctx context.Context) (int64, error) {
|
||||
result, err := db.GetEngine(ctx).Exec("UPDATE `action_runner` SET `owner_id` = 0 WHERE `repo_id` > 0 AND `owner_id` > 0")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.RowsAffected()
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ func GetRunnerToken(ctx context.Context, token string) (*ActionRunnerToken, erro
|
|||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, fmt.Errorf("runner token %q: %w", token, util.ErrNotExist)
|
||||
return nil, fmt.Errorf(`runner token "%s...": %w`, util.TruncateRunes(token, 3), util.ErrNotExist)
|
||||
}
|
||||
return &runnerToken, nil
|
||||
}
|
||||
|
@ -68,19 +68,15 @@ func UpdateRunnerToken(ctx context.Context, r *ActionRunnerToken, cols ...string
|
|||
return err
|
||||
}
|
||||
|
||||
// NewRunnerToken creates a new active runner token and invalidate all old tokens
|
||||
// NewRunnerTokenWithValue creates a new active runner token and invalidate all old tokens
|
||||
// ownerID will be ignored and treated as 0 if repoID is non-zero.
|
||||
func NewRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerToken, error) {
|
||||
func NewRunnerTokenWithValue(ctx context.Context, ownerID, repoID int64, token string) (*ActionRunnerToken, error) {
|
||||
if ownerID != 0 && repoID != 0 {
|
||||
// It's trying to create a runner token that belongs to a repository, but OwnerID has been set accidentally.
|
||||
// Remove OwnerID to avoid confusion; it's not worth returning an error here.
|
||||
ownerID = 0
|
||||
}
|
||||
|
||||
token, err := util.CryptoRandomString(40)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
runnerToken := &ActionRunnerToken{
|
||||
OwnerID: ownerID,
|
||||
RepoID: repoID,
|
||||
|
@ -95,11 +91,19 @@ func NewRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerTo
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = db.GetEngine(ctx).Insert(runnerToken)
|
||||
_, err := db.GetEngine(ctx).Insert(runnerToken)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func NewRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerToken, error) {
|
||||
token, err := util.CryptoRandomString(40)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewRunnerTokenWithValue(ctx, ownerID, repoID, token)
|
||||
}
|
||||
|
||||
// GetLatestRunnerToken returns the latest runner token
|
||||
func GetLatestRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerToken, error) {
|
||||
if ownerID != 0 && repoID != 0 {
|
||||
|
|
|
@ -17,7 +17,7 @@ func TestGetLatestRunnerToken(t *testing.T) {
|
|||
token := unittest.AssertExistsAndLoadBean(t, &ActionRunnerToken{ID: 3})
|
||||
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, token, expectedToken)
|
||||
assert.EqualValues(t, expectedToken, token)
|
||||
}
|
||||
|
||||
func TestNewRunnerToken(t *testing.T) {
|
||||
|
@ -26,7 +26,7 @@ func TestNewRunnerToken(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, token, expectedToken)
|
||||
assert.EqualValues(t, expectedToken, token)
|
||||
}
|
||||
|
||||
func TestUpdateRunnerToken(t *testing.T) {
|
||||
|
@ -36,5 +36,5 @@ func TestUpdateRunnerToken(t *testing.T) {
|
|||
assert.NoError(t, UpdateRunnerToken(db.DefaultContext, token))
|
||||
expectedToken, err := GetLatestRunnerToken(db.DefaultContext, 1, 0)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, token, expectedToken)
|
||||
assert.EqualValues(t, expectedToken, token)
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
webhook_module "code.gitea.io/gitea/modules/webhook"
|
||||
)
|
||||
|
||||
|
@ -42,15 +43,12 @@ func init() {
|
|||
// GetSchedulesMapByIDs returns the schedules by given id slice.
|
||||
func GetSchedulesMapByIDs(ctx context.Context, ids []int64) (map[int64]*ActionSchedule, error) {
|
||||
schedules := make(map[int64]*ActionSchedule, len(ids))
|
||||
if len(ids) == 0 {
|
||||
return schedules, nil
|
||||
}
|
||||
return schedules, db.GetEngine(ctx).In("id", ids).Find(&schedules)
|
||||
}
|
||||
|
||||
// GetReposMapByIDs returns the repos by given id slice.
|
||||
func GetReposMapByIDs(ctx context.Context, ids []int64) (map[int64]*repo_model.Repository, error) {
|
||||
repos := make(map[int64]*repo_model.Repository, len(ids))
|
||||
return repos, db.GetEngine(ctx).In("id", ids).Find(&repos)
|
||||
}
|
||||
|
||||
// CreateScheduleTask creates new schedule task.
|
||||
func CreateScheduleTask(ctx context.Context, rows []*ActionSchedule) error {
|
||||
// Return early if there are no rows to insert
|
||||
|
@ -67,6 +65,7 @@ func CreateScheduleTask(ctx context.Context, rows []*ActionSchedule) error {
|
|||
|
||||
// Loop through each schedule row
|
||||
for _, row := range rows {
|
||||
row.Title = util.EllipsisDisplayString(row.Title, 255)
|
||||
// Create new schedule row
|
||||
if err = db.Insert(ctx, row); err != nil {
|
||||
return err
|
||||
|
@ -118,21 +117,22 @@ func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error {
|
|||
return committer.Commit()
|
||||
}
|
||||
|
||||
func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) error {
|
||||
func CleanRepoScheduleTasks(ctx context.Context, repo *repo_model.Repository) ([]*ActionRunJob, error) {
|
||||
// If actions disabled when there is schedule task, this will remove the outdated schedule tasks
|
||||
// There is no other place we can do this because the app.ini will be changed manually
|
||||
if err := DeleteScheduleTaskByRepo(ctx, repo.ID); err != nil {
|
||||
return fmt.Errorf("DeleteCronTaskByRepo: %v", err)
|
||||
return nil, fmt.Errorf("DeleteCronTaskByRepo: %v", err)
|
||||
}
|
||||
// cancel running cron jobs of this repository and delete old schedules
|
||||
if err := CancelPreviousJobs(
|
||||
jobs, err := CancelPreviousJobs(
|
||||
ctx,
|
||||
repo.ID,
|
||||
repo.DefaultBranch,
|
||||
"",
|
||||
webhook_module.HookEventSchedule,
|
||||
); err != nil {
|
||||
return fmt.Errorf("CancelPreviousJobs: %v", err)
|
||||
)
|
||||
if err != nil {
|
||||
return jobs, fmt.Errorf("CancelPreviousJobs: %v", err)
|
||||
}
|
||||
return nil
|
||||
return jobs, nil
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ func (specs SpecList) LoadSchedules(ctx context.Context) error {
|
|||
}
|
||||
|
||||
repoIDs := specs.GetRepoIDs()
|
||||
repos, err := GetReposMapByIDs(ctx, repoIDs)
|
||||
repos, err := repo_model.GetRepositoriesMapByIDs(ctx, repoIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -7,19 +7,17 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestActionScheduleSpec_Parse(t *testing.T) {
|
||||
// Mock the local timezone is not UTC
|
||||
local := time.Local
|
||||
tz, err := time.LoadLocation("Asia/Shanghai")
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
time.Local = local
|
||||
}()
|
||||
time.Local = tz
|
||||
defer test.MockVariableValue(&time.Local, tz)()
|
||||
|
||||
now, err := time.Parse(time.RFC3339, "2024-07-31T15:47:55+08:00")
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -298,7 +298,7 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
|
|||
if len(workflowJob.Steps) > 0 {
|
||||
steps := make([]*ActionTaskStep, len(workflowJob.Steps))
|
||||
for i, v := range workflowJob.Steps {
|
||||
name, _ := util.SplitStringAtByteN(v.String(), 255)
|
||||
name := util.EllipsisDisplayString(v.String(), 255)
|
||||
steps[i] = &ActionTaskStep{
|
||||
Name: name,
|
||||
TaskID: task.ID,
|
||||
|
@ -341,7 +341,7 @@ func UpdateTask(ctx context.Context, task *ActionTask, cols ...string) error {
|
|||
// UpdateTaskByState updates the task by the state.
|
||||
// It will always update the task if the state is not final, even there is no change.
|
||||
// So it will update ActionTask.Updated to avoid the task being judged as a zombie task.
|
||||
func UpdateTaskByState(ctx context.Context, state *runnerv1.TaskState) (*ActionTask, error) {
|
||||
func UpdateTaskByState(ctx context.Context, runnerID int64, state *runnerv1.TaskState) (*ActionTask, error) {
|
||||
stepStates := map[int64]*runnerv1.StepState{}
|
||||
for _, v := range state.Steps {
|
||||
stepStates[v.Id] = v
|
||||
|
@ -360,6 +360,8 @@ func UpdateTaskByState(ctx context.Context, state *runnerv1.TaskState) (*ActionT
|
|||
return nil, err
|
||||
} else if !has {
|
||||
return nil, util.ErrNotExist
|
||||
} else if runnerID != task.RunnerID {
|
||||
return nil, fmt.Errorf("invalid runner for task")
|
||||
}
|
||||
|
||||
if task.Status.IsDone() {
|
||||
|
|
|
@ -58,6 +58,7 @@ func InsertVariable(ctx context.Context, ownerID, repoID int64, name, data strin
|
|||
|
||||
type FindVariablesOpts struct {
|
||||
db.ListOptions
|
||||
IDs []int64
|
||||
RepoID int64
|
||||
OwnerID int64 // it will be ignored if RepoID is set
|
||||
Name string
|
||||
|
@ -65,6 +66,15 @@ type FindVariablesOpts struct {
|
|||
|
||||
func (opts FindVariablesOpts) ToConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
|
||||
if len(opts.IDs) > 0 {
|
||||
if len(opts.IDs) == 1 {
|
||||
cond = cond.And(builder.Eq{"id": opts.IDs[0]})
|
||||
} else {
|
||||
cond = cond.And(builder.In("id", opts.IDs))
|
||||
}
|
||||
}
|
||||
|
||||
// Since we now support instance-level variables,
|
||||
// there is no need to check for null values for `owner_id` and `repo_id`
|
||||
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
|
||||
|
@ -85,12 +95,12 @@ func FindVariables(ctx context.Context, opts FindVariablesOpts) ([]*ActionVariab
|
|||
return db.Find[ActionVariable](ctx, opts)
|
||||
}
|
||||
|
||||
func UpdateVariable(ctx context.Context, variable *ActionVariable) (bool, error) {
|
||||
count, err := db.GetEngine(ctx).ID(variable.ID).Cols("name", "data").
|
||||
Update(&ActionVariable{
|
||||
Name: variable.Name,
|
||||
Data: variable.Data,
|
||||
})
|
||||
func UpdateVariableCols(ctx context.Context, variable *ActionVariable, cols ...string) (bool, error) {
|
||||
variable.Name = strings.ToUpper(variable.Name)
|
||||
count, err := db.GetEngine(ctx).
|
||||
ID(variable.ID).
|
||||
Cols(cols...).
|
||||
Update(variable)
|
||||
return count != 0, err
|
||||
}
|
||||
|
||||
|
@ -137,3 +147,17 @@ func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string,
|
|||
|
||||
return variables, nil
|
||||
}
|
||||
|
||||
func CountWrongRepoLevelVariables(ctx context.Context) (int64, error) {
|
||||
var result int64
|
||||
_, err := db.GetEngine(ctx).SQL("SELECT count(`id`) FROM `action_variable` WHERE `repo_id` > 0 AND `owner_id` > 0").Get(&result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func UpdateWrongRepoLevelVariables(ctx context.Context) (int64, error) {
|
||||
result, err := db.GetEngine(ctx).Exec("UPDATE `action_variable` SET `owner_id` = 0 WHERE `repo_id` > 0 AND `owner_id` > 0")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.RowsAffected()
|
||||
}
|
||||
|
|
|
@ -16,16 +16,14 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm/schemas"
|
||||
|
@ -72,9 +70,9 @@ func (at ActionType) String() string {
|
|||
case ActionRenameRepo:
|
||||
return "rename_repo"
|
||||
case ActionStarRepo:
|
||||
return "star_repo"
|
||||
return "star_repo" // will not displayed in feeds.tmpl
|
||||
case ActionWatchRepo:
|
||||
return "watch_repo"
|
||||
return "watch_repo" // will not displayed in feeds.tmpl
|
||||
case ActionCommitRepo:
|
||||
return "commit_repo"
|
||||
case ActionCreateIssue:
|
||||
|
@ -171,7 +169,10 @@ func (a *Action) TableIndices() []*schemas.Index {
|
|||
cudIndex := schemas.NewIndex("c_u_d", schemas.IndexType)
|
||||
cudIndex.AddColumn("created_unix", "user_id", "is_deleted")
|
||||
|
||||
indices := []*schemas.Index{actUserIndex, repoIndex, cudIndex}
|
||||
cuIndex := schemas.NewIndex("c_u", schemas.IndexType)
|
||||
cuIndex.AddColumn("user_id", "is_deleted")
|
||||
|
||||
indices := []*schemas.Index{actUserIndex, repoIndex, cudIndex, cuIndex}
|
||||
|
||||
return indices
|
||||
}
|
||||
|
@ -197,15 +198,13 @@ func (a *Action) LoadActUser(ctx context.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
func (a *Action) loadRepo(ctx context.Context) {
|
||||
func (a *Action) LoadRepo(ctx context.Context) error {
|
||||
if a.Repo != nil {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
var err error
|
||||
a.Repo, err = repo_model.GetRepositoryByID(ctx, a.RepoID)
|
||||
if err != nil {
|
||||
log.Error("repo_model.GetRepositoryByID(%d): %v", a.RepoID, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// GetActFullName gets the action's user full name.
|
||||
|
@ -223,7 +222,7 @@ func (a *Action) GetActUserName(ctx context.Context) string {
|
|||
// ShortActUserName gets the action's user name trimmed to max 20
|
||||
// chars.
|
||||
func (a *Action) ShortActUserName(ctx context.Context) string {
|
||||
return base.EllipsisString(a.GetActUserName(ctx), 20)
|
||||
return util.EllipsisDisplayString(a.GetActUserName(ctx), 20)
|
||||
}
|
||||
|
||||
// GetActDisplayName gets the action's display name based on DEFAULT_SHOW_FULL_NAME, or falls back to the username if it is blank.
|
||||
|
@ -247,26 +246,32 @@ func (a *Action) GetActDisplayNameTitle(ctx context.Context) string {
|
|||
|
||||
// GetRepoUserName returns the name of the action repository owner.
|
||||
func (a *Action) GetRepoUserName(ctx context.Context) string {
|
||||
a.loadRepo(ctx)
|
||||
_ = a.LoadRepo(ctx)
|
||||
if a.Repo == nil {
|
||||
return "(non-existing-repo)"
|
||||
}
|
||||
return a.Repo.OwnerName
|
||||
}
|
||||
|
||||
// ShortRepoUserName returns the name of the action repository owner
|
||||
// trimmed to max 20 chars.
|
||||
func (a *Action) ShortRepoUserName(ctx context.Context) string {
|
||||
return base.EllipsisString(a.GetRepoUserName(ctx), 20)
|
||||
return util.EllipsisDisplayString(a.GetRepoUserName(ctx), 20)
|
||||
}
|
||||
|
||||
// GetRepoName returns the name of the action repository.
|
||||
func (a *Action) GetRepoName(ctx context.Context) string {
|
||||
a.loadRepo(ctx)
|
||||
_ = a.LoadRepo(ctx)
|
||||
if a.Repo == nil {
|
||||
return "(non-existing-repo)"
|
||||
}
|
||||
return a.Repo.Name
|
||||
}
|
||||
|
||||
// ShortRepoName returns the name of the action repository
|
||||
// trimmed to max 33 chars.
|
||||
func (a *Action) ShortRepoName(ctx context.Context) string {
|
||||
return base.EllipsisString(a.GetRepoName(ctx), 33)
|
||||
return util.EllipsisDisplayString(a.GetRepoName(ctx), 33)
|
||||
}
|
||||
|
||||
// GetRepoPath returns the virtual path to the action repository.
|
||||
|
@ -346,7 +351,7 @@ func (a *Action) GetBranch() string {
|
|||
|
||||
// GetRefLink returns the action's ref link.
|
||||
func (a *Action) GetRefLink(ctx context.Context) string {
|
||||
return git.RefURL(a.GetRepoLink(ctx), a.RefName)
|
||||
return a.GetRepoLink(ctx) + "/src/" + git.RefName(a.RefName).RefWebLinkPath()
|
||||
}
|
||||
|
||||
// GetTag returns the action's repository tag.
|
||||
|
@ -439,65 +444,31 @@ type GetFeedsOptions struct {
|
|||
Date string // the day we want activity for: YYYY-MM-DD
|
||||
}
|
||||
|
||||
// GetFeeds returns actions according to the provided options
|
||||
func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, error) {
|
||||
if opts.RequestedUser == nil && opts.RequestedTeam == nil && opts.RequestedRepo == nil {
|
||||
return nil, 0, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
|
||||
}
|
||||
|
||||
cond, err := activityQueryCondition(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
actions := make([]*Action, 0, opts.PageSize)
|
||||
var count int64
|
||||
opts.SetDefaultValues()
|
||||
|
||||
if opts.Page < 10 { // TODO: why it's 10 but other values? It's an experience value.
|
||||
sess := db.GetEngine(ctx).Where(cond)
|
||||
sess = db.SetSessionPagination(sess, &opts)
|
||||
|
||||
count, err = sess.Desc("`action`.created_unix").FindAndCount(&actions)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("FindAndCount: %w", err)
|
||||
}
|
||||
} else {
|
||||
// First, only query which IDs are necessary, and only then query all actions to speed up the overall query
|
||||
sess := db.GetEngine(ctx).Where(cond).Select("`action`.id")
|
||||
sess = db.SetSessionPagination(sess, &opts)
|
||||
|
||||
actionIDs := make([]int64, 0, opts.PageSize)
|
||||
if err := sess.Table("action").Desc("`action`.created_unix").Find(&actionIDs); err != nil {
|
||||
return nil, 0, fmt.Errorf("Find(actionsIDs): %w", err)
|
||||
}
|
||||
|
||||
count, err = db.GetEngine(ctx).Where(cond).
|
||||
Table("action").
|
||||
Cols("`action`.id").Count()
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("Count: %w", err)
|
||||
}
|
||||
|
||||
if err := db.GetEngine(ctx).In("`action`.id", actionIDs).Desc("`action`.created_unix").Find(&actions); err != nil {
|
||||
return nil, 0, fmt.Errorf("Find: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := ActionList(actions).LoadAttributes(ctx); err != nil {
|
||||
return nil, 0, fmt.Errorf("LoadAttributes: %w", err)
|
||||
}
|
||||
|
||||
return actions, count, nil
|
||||
}
|
||||
|
||||
// ActivityReadable return whether doer can read activities of user
|
||||
func ActivityReadable(user, doer *user_model.User) bool {
|
||||
return !user.KeepActivityPrivate ||
|
||||
doer != nil && (doer.IsAdmin || user.ID == doer.ID)
|
||||
}
|
||||
|
||||
func activityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.Cond, error) {
|
||||
func FeedDateCond(opts GetFeedsOptions) builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if opts.Date == "" {
|
||||
return cond
|
||||
}
|
||||
|
||||
dateLow, err := time.ParseInLocation("2006-01-02", opts.Date, setting.DefaultUILocation)
|
||||
if err != nil {
|
||||
log.Warn("Unable to parse %s, filter not applied: %v", opts.Date, err)
|
||||
} else {
|
||||
dateHigh := dateLow.Add(86399000000000) // 23h59m59s
|
||||
|
||||
cond = cond.And(builder.Gte{"`action`.created_unix": dateLow.Unix()})
|
||||
cond = cond.And(builder.Lte{"`action`.created_unix": dateHigh.Unix()})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
func ActivityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.Cond, error) {
|
||||
cond := builder.NewCond()
|
||||
|
||||
if opts.RequestedTeam != nil && opts.RequestedUser == nil {
|
||||
|
@ -554,8 +525,8 @@ func activityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.
|
|||
}
|
||||
|
||||
if opts.RequestedTeam != nil {
|
||||
env := organization.OrgFromUser(opts.RequestedUser).AccessibleTeamReposEnv(ctx, opts.RequestedTeam)
|
||||
teamRepoIDs, err := env.RepoIDs(1, opts.RequestedUser.NumRepos)
|
||||
env := repo_model.AccessibleTeamReposEnv(organization.OrgFromUser(opts.RequestedUser), opts.RequestedTeam)
|
||||
teamRepoIDs, err := env.RepoIDs(ctx, 1, opts.RequestedUser.NumRepos)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetTeamRepositories: %w", err)
|
||||
}
|
||||
|
@ -577,17 +548,7 @@ func activityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.
|
|||
cond = cond.And(builder.Eq{"is_deleted": false})
|
||||
}
|
||||
|
||||
if opts.Date != "" {
|
||||
dateLow, err := time.ParseInLocation("2006-01-02", opts.Date, setting.DefaultUILocation)
|
||||
if err != nil {
|
||||
log.Warn("Unable to parse %s, filter not applied: %v", opts.Date, err)
|
||||
} else {
|
||||
dateHigh := dateLow.Add(86399000000000) // 23h59m59s
|
||||
|
||||
cond = cond.And(builder.Gte{"`action`.created_unix": dateLow.Unix()})
|
||||
cond = cond.And(builder.Lte{"`action`.created_unix": dateHigh.Unix()})
|
||||
}
|
||||
}
|
||||
cond = cond.And(FeedDateCond(opts))
|
||||
|
||||
return cond, nil
|
||||
}
|
||||
|
@ -602,130 +563,6 @@ func DeleteOldActions(ctx context.Context, olderThan time.Duration) (err error)
|
|||
return err
|
||||
}
|
||||
|
||||
// NotifyWatchers creates batch of actions for every watcher.
|
||||
// It could insert duplicate actions for a repository action, like this:
|
||||
// * Original action: UserID=1 (the real actor), ActUserID=1
|
||||
// * Organization action: UserID=100 (the repo's org), ActUserID=1
|
||||
// * Watcher action: UserID=20 (a user who is watching a repo), ActUserID=1
|
||||
func NotifyWatchers(ctx context.Context, actions ...*Action) error {
|
||||
var watchers []*repo_model.Watch
|
||||
var repo *repo_model.Repository
|
||||
var err error
|
||||
var permCode []bool
|
||||
var permIssue []bool
|
||||
var permPR []bool
|
||||
|
||||
e := db.GetEngine(ctx)
|
||||
|
||||
for _, act := range actions {
|
||||
repoChanged := repo == nil || repo.ID != act.RepoID
|
||||
|
||||
if repoChanged {
|
||||
// Add feeds for user self and all watchers.
|
||||
watchers, err = repo_model.GetWatchers(ctx, act.RepoID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get watchers: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Add feed for actioner.
|
||||
act.UserID = act.ActUserID
|
||||
if _, err = e.Insert(act); err != nil {
|
||||
return fmt.Errorf("insert new actioner: %w", err)
|
||||
}
|
||||
|
||||
if repoChanged {
|
||||
act.loadRepo(ctx)
|
||||
repo = act.Repo
|
||||
|
||||
// check repo owner exist.
|
||||
if err := act.Repo.LoadOwner(ctx); err != nil {
|
||||
return fmt.Errorf("can't get repo owner: %w", err)
|
||||
}
|
||||
} else if act.Repo == nil {
|
||||
act.Repo = repo
|
||||
}
|
||||
|
||||
// Add feed for organization
|
||||
if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID {
|
||||
act.ID = 0
|
||||
act.UserID = act.Repo.Owner.ID
|
||||
if err = db.Insert(ctx, act); err != nil {
|
||||
return fmt.Errorf("insert new actioner: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if repoChanged {
|
||||
permCode = make([]bool, len(watchers))
|
||||
permIssue = make([]bool, len(watchers))
|
||||
permPR = make([]bool, len(watchers))
|
||||
for i, watcher := range watchers {
|
||||
user, err := user_model.GetUserByID(ctx, watcher.UserID)
|
||||
if err != nil {
|
||||
permCode[i] = false
|
||||
permIssue[i] = false
|
||||
permPR[i] = false
|
||||
continue
|
||||
}
|
||||
perm, err := access_model.GetUserRepoPermission(ctx, repo, user)
|
||||
if err != nil {
|
||||
permCode[i] = false
|
||||
permIssue[i] = false
|
||||
permPR[i] = false
|
||||
continue
|
||||
}
|
||||
permCode[i] = perm.CanRead(unit.TypeCode)
|
||||
permIssue[i] = perm.CanRead(unit.TypeIssues)
|
||||
permPR[i] = perm.CanRead(unit.TypePullRequests)
|
||||
}
|
||||
}
|
||||
|
||||
for i, watcher := range watchers {
|
||||
if act.ActUserID == watcher.UserID {
|
||||
continue
|
||||
}
|
||||
act.ID = 0
|
||||
act.UserID = watcher.UserID
|
||||
act.Repo.Units = nil
|
||||
|
||||
switch act.OpType {
|
||||
case ActionCommitRepo, ActionPushTag, ActionDeleteTag, ActionPublishRelease, ActionDeleteBranch:
|
||||
if !permCode[i] {
|
||||
continue
|
||||
}
|
||||
case ActionCreateIssue, ActionCommentIssue, ActionCloseIssue, ActionReopenIssue:
|
||||
if !permIssue[i] {
|
||||
continue
|
||||
}
|
||||
case ActionCreatePullRequest, ActionCommentPull, ActionMergePullRequest, ActionClosePullRequest, ActionReopenPullRequest, ActionAutoMergePullRequest:
|
||||
if !permPR[i] {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if err = db.Insert(ctx, act); err != nil {
|
||||
return fmt.Errorf("insert new action: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NotifyWatchersActions creates batch of actions for every watcher.
|
||||
func NotifyWatchersActions(ctx context.Context, acts []*Action) error {
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer committer.Close()
|
||||
for _, act := range acts {
|
||||
if err := NotifyWatchers(ctx, act); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return committer.Commit()
|
||||
}
|
||||
|
||||
// DeleteIssueActions delete all actions related with issueID
|
||||
func DeleteIssueActions(ctx context.Context, repoID, issueID, issueIndex int64) error {
|
||||
// delete actions assigned to this issue
|
||||
|
@ -761,7 +598,7 @@ func DeleteIssueActions(ctx context.Context, repoID, issueID, issueIndex int64)
|
|||
// CountActionCreatedUnixString count actions where created_unix is an empty string
|
||||
func CountActionCreatedUnixString(ctx context.Context) (int64, error) {
|
||||
if setting.Database.Type.IsSQLite3() {
|
||||
return db.GetEngine(ctx).Where(`created_unix = ""`).Count(new(Action))
|
||||
return db.GetEngine(ctx).Where(`created_unix = ''`).Count(new(Action))
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
@ -769,7 +606,7 @@ func CountActionCreatedUnixString(ctx context.Context) (int64, error) {
|
|||
// FixActionCreatedUnixString set created_unix to zero if it is an empty string
|
||||
func FixActionCreatedUnixString(ctx context.Context) (int64, error) {
|
||||
if setting.Database.Type.IsSQLite3() {
|
||||
res, err := db.GetEngine(ctx).Exec(`UPDATE action SET created_unix = 0 WHERE created_unix = ""`)
|
||||
res, err := db.GetEngine(ctx).Exec(`UPDATE action SET created_unix = 0 WHERE created_unix = ''`)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
|
@ -201,3 +201,77 @@ func (actions ActionList) LoadIssues(ctx context.Context) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFeeds returns actions according to the provided options
|
||||
func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, error) {
|
||||
if opts.RequestedUser == nil && opts.RequestedTeam == nil && opts.RequestedRepo == nil {
|
||||
return nil, 0, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
|
||||
}
|
||||
|
||||
var err error
|
||||
var cond builder.Cond
|
||||
// if the actor is the requested user or is an administrator, we can skip the ActivityQueryCondition
|
||||
if opts.Actor != nil && opts.RequestedUser != nil && (opts.Actor.IsAdmin || opts.Actor.ID == opts.RequestedUser.ID) {
|
||||
cond = builder.Eq{
|
||||
"user_id": opts.RequestedUser.ID,
|
||||
}.And(
|
||||
FeedDateCond(opts),
|
||||
)
|
||||
|
||||
if !opts.IncludeDeleted {
|
||||
cond = cond.And(builder.Eq{"is_deleted": false})
|
||||
}
|
||||
|
||||
if !opts.IncludePrivate {
|
||||
cond = cond.And(builder.Eq{"is_private": false})
|
||||
}
|
||||
if opts.OnlyPerformedBy {
|
||||
cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID})
|
||||
}
|
||||
} else {
|
||||
cond, err = ActivityQueryCondition(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
actions := make([]*Action, 0, opts.PageSize)
|
||||
var count int64
|
||||
opts.SetDefaultValues()
|
||||
|
||||
if opts.Page < 10 { // TODO: why it's 10 but other values? It's an experience value.
|
||||
sess := db.GetEngine(ctx).Where(cond)
|
||||
sess = db.SetSessionPagination(sess, &opts)
|
||||
|
||||
count, err = sess.Desc("`action`.created_unix").FindAndCount(&actions)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("FindAndCount: %w", err)
|
||||
}
|
||||
} else {
|
||||
// First, only query which IDs are necessary, and only then query all actions to speed up the overall query
|
||||
sess := db.GetEngine(ctx).Where(cond).Select("`action`.id")
|
||||
sess = db.SetSessionPagination(sess, &opts)
|
||||
|
||||
actionIDs := make([]int64, 0, opts.PageSize)
|
||||
if err := sess.Table("action").Desc("`action`.created_unix").Find(&actionIDs); err != nil {
|
||||
return nil, 0, fmt.Errorf("Find(actionsIDs): %w", err)
|
||||
}
|
||||
|
||||
count, err = db.GetEngine(ctx).Where(cond).
|
||||
Table("action").
|
||||
Cols("`action`.id").Count()
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("Count: %w", err)
|
||||
}
|
||||
|
||||
if err := db.GetEngine(ctx).In("`action`.id", actionIDs).Desc("`action`.created_unix").Find(&actions); err != nil {
|
||||
return nil, 0, fmt.Errorf("Find: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := ActionList(actions).LoadAttributes(ctx); err != nil {
|
||||
return nil, 0, fmt.Errorf("LoadAttributes: %w", err)
|
||||
}
|
||||
|
||||
return actions, count, nil
|
||||
}
|
||||
|
|
|
@ -42,114 +42,6 @@ func TestAction_GetRepoLink(t *testing.T) {
|
|||
assert.Equal(t, comment.HTMLURL(db.DefaultContext), action.GetCommentHTMLURL(db.DefaultContext))
|
||||
}
|
||||
|
||||
func TestGetFeeds(t *testing.T) {
|
||||
// test with an individual user
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
|
||||
actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedUser: user,
|
||||
Actor: user,
|
||||
IncludePrivate: true,
|
||||
OnlyPerformedBy: false,
|
||||
IncludeDeleted: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, actions, 1) {
|
||||
assert.EqualValues(t, 1, actions[0].ID)
|
||||
assert.EqualValues(t, user.ID, actions[0].UserID)
|
||||
}
|
||||
assert.Equal(t, int64(1), count)
|
||||
|
||||
actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedUser: user,
|
||||
Actor: user,
|
||||
IncludePrivate: false,
|
||||
OnlyPerformedBy: false,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 0)
|
||||
assert.Equal(t, int64(0), count)
|
||||
}
|
||||
|
||||
func TestGetFeedsForRepos(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
privRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
pubRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 8})
|
||||
|
||||
// private repo & no login
|
||||
actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedRepo: privRepo,
|
||||
IncludePrivate: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 0)
|
||||
assert.Equal(t, int64(0), count)
|
||||
|
||||
// public repo & no login
|
||||
actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedRepo: pubRepo,
|
||||
IncludePrivate: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 1)
|
||||
assert.Equal(t, int64(1), count)
|
||||
|
||||
// private repo and login
|
||||
actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedRepo: privRepo,
|
||||
IncludePrivate: true,
|
||||
Actor: user,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 1)
|
||||
assert.Equal(t, int64(1), count)
|
||||
|
||||
// public repo & login
|
||||
actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedRepo: pubRepo,
|
||||
IncludePrivate: true,
|
||||
Actor: user,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 1)
|
||||
assert.Equal(t, int64(1), count)
|
||||
}
|
||||
|
||||
func TestGetFeeds2(t *testing.T) {
|
||||
// test with an organization user
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
|
||||
actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedUser: org,
|
||||
Actor: user,
|
||||
IncludePrivate: true,
|
||||
OnlyPerformedBy: false,
|
||||
IncludeDeleted: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 1)
|
||||
if assert.Len(t, actions, 1) {
|
||||
assert.EqualValues(t, 2, actions[0].ID)
|
||||
assert.EqualValues(t, org.ID, actions[0].UserID)
|
||||
}
|
||||
assert.Equal(t, int64(1), count)
|
||||
|
||||
actions, count, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedUser: org,
|
||||
Actor: user,
|
||||
IncludePrivate: false,
|
||||
OnlyPerformedBy: false,
|
||||
IncludeDeleted: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 0)
|
||||
assert.Equal(t, int64(0), count)
|
||||
}
|
||||
|
||||
func TestActivityReadable(t *testing.T) {
|
||||
tt := []struct {
|
||||
desc string
|
||||
|
@ -190,63 +82,6 @@ func TestActivityReadable(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNotifyWatchers(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
action := &activities_model.Action{
|
||||
ActUserID: 8,
|
||||
RepoID: 1,
|
||||
OpType: activities_model.ActionStarRepo,
|
||||
}
|
||||
assert.NoError(t, activities_model.NotifyWatchers(db.DefaultContext, action))
|
||||
|
||||
// One watchers are inactive, thus action is only created for user 8, 1, 4, 11
|
||||
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
|
||||
ActUserID: action.ActUserID,
|
||||
UserID: 8,
|
||||
RepoID: action.RepoID,
|
||||
OpType: action.OpType,
|
||||
})
|
||||
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
|
||||
ActUserID: action.ActUserID,
|
||||
UserID: 1,
|
||||
RepoID: action.RepoID,
|
||||
OpType: action.OpType,
|
||||
})
|
||||
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
|
||||
ActUserID: action.ActUserID,
|
||||
UserID: 4,
|
||||
RepoID: action.RepoID,
|
||||
OpType: action.OpType,
|
||||
})
|
||||
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
|
||||
ActUserID: action.ActUserID,
|
||||
UserID: 11,
|
||||
RepoID: action.RepoID,
|
||||
OpType: action.OpType,
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetFeedsCorrupted(t *testing.T) {
|
||||
// Now we will not check for corrupted data in the feeds
|
||||
// users should run doctor to fix their data
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
|
||||
ID: 8,
|
||||
RepoID: 1700,
|
||||
})
|
||||
|
||||
actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedUser: user,
|
||||
Actor: user,
|
||||
IncludePrivate: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 1)
|
||||
assert.Equal(t, int64(1), count)
|
||||
}
|
||||
|
||||
func TestConsistencyUpdateAction(t *testing.T) {
|
||||
if !setting.Database.Type.IsSQLite3() {
|
||||
t.Skip("Test is only for SQLite database.")
|
||||
|
@ -256,7 +91,7 @@ func TestConsistencyUpdateAction(t *testing.T) {
|
|||
unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
|
||||
ID: int64(id),
|
||||
})
|
||||
_, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = "" WHERE id = ?`, id)
|
||||
_, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = '' WHERE id = ?`, id)
|
||||
assert.NoError(t, err)
|
||||
actions := make([]*activities_model.Action, 0, 1)
|
||||
//
|
||||
|
@ -322,24 +157,3 @@ func TestDeleteIssueActions(t *testing.T) {
|
|||
assert.NoError(t, activities_model.DeleteIssueActions(db.DefaultContext, issue.RepoID, issue.ID, issue.Index))
|
||||
unittest.AssertCount(t, &activities_model.Action{}, 0)
|
||||
}
|
||||
|
||||
func TestRepoActions(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
_ = db.TruncateBeans(db.DefaultContext, &activities_model.Action{})
|
||||
for i := 0; i < 3; i++ {
|
||||
_ = db.Insert(db.DefaultContext, &activities_model.Action{
|
||||
UserID: 2 + int64(i),
|
||||
ActUserID: 2,
|
||||
RepoID: repo.ID,
|
||||
OpType: activities_model.ActionCommentIssue,
|
||||
})
|
||||
}
|
||||
count, _ := db.Count[activities_model.Action](db.DefaultContext, &db.ListOptions{})
|
||||
assert.EqualValues(t, 3, count)
|
||||
actions, _, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
|
||||
RequestedRepo: repo,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, 1)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -50,25 +51,64 @@ const (
|
|||
// Notification represents a notification
|
||||
type Notification struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UserID int64 `xorm:"INDEX NOT NULL"`
|
||||
RepoID int64 `xorm:"INDEX NOT NULL"`
|
||||
UserID int64 `xorm:"NOT NULL"`
|
||||
RepoID int64 `xorm:"NOT NULL"`
|
||||
|
||||
Status NotificationStatus `xorm:"SMALLINT INDEX NOT NULL"`
|
||||
Source NotificationSource `xorm:"SMALLINT INDEX NOT NULL"`
|
||||
Status NotificationStatus `xorm:"SMALLINT NOT NULL"`
|
||||
Source NotificationSource `xorm:"SMALLINT NOT NULL"`
|
||||
|
||||
IssueID int64 `xorm:"INDEX NOT NULL"`
|
||||
CommitID string `xorm:"INDEX"`
|
||||
IssueID int64 `xorm:"NOT NULL"`
|
||||
CommitID string
|
||||
CommentID int64
|
||||
|
||||
UpdatedBy int64 `xorm:"INDEX NOT NULL"`
|
||||
UpdatedBy int64 `xorm:"NOT NULL"`
|
||||
|
||||
Issue *issues_model.Issue `xorm:"-"`
|
||||
Repository *repo_model.Repository `xorm:"-"`
|
||||
Comment *issues_model.Comment `xorm:"-"`
|
||||
User *user_model.User `xorm:"-"`
|
||||
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created INDEX NOT NULL"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated INDEX NOT NULL"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated NOT NULL"`
|
||||
}
|
||||
|
||||
// TableIndices implements xorm's TableIndices interface
|
||||
func (n *Notification) TableIndices() []*schemas.Index {
|
||||
indices := make([]*schemas.Index, 0, 8)
|
||||
usuuIndex := schemas.NewIndex("u_s_uu", schemas.IndexType)
|
||||
usuuIndex.AddColumn("user_id", "status", "updated_unix")
|
||||
indices = append(indices, usuuIndex)
|
||||
|
||||
// Add the individual indices that were previously defined in struct tags
|
||||
userIDIndex := schemas.NewIndex("idx_notification_user_id", schemas.IndexType)
|
||||
userIDIndex.AddColumn("user_id")
|
||||
indices = append(indices, userIDIndex)
|
||||
|
||||
repoIDIndex := schemas.NewIndex("idx_notification_repo_id", schemas.IndexType)
|
||||
repoIDIndex.AddColumn("repo_id")
|
||||
indices = append(indices, repoIDIndex)
|
||||
|
||||
statusIndex := schemas.NewIndex("idx_notification_status", schemas.IndexType)
|
||||
statusIndex.AddColumn("status")
|
||||
indices = append(indices, statusIndex)
|
||||
|
||||
sourceIndex := schemas.NewIndex("idx_notification_source", schemas.IndexType)
|
||||
sourceIndex.AddColumn("source")
|
||||
indices = append(indices, sourceIndex)
|
||||
|
||||
issueIDIndex := schemas.NewIndex("idx_notification_issue_id", schemas.IndexType)
|
||||
issueIDIndex.AddColumn("issue_id")
|
||||
indices = append(indices, issueIDIndex)
|
||||
|
||||
commitIDIndex := schemas.NewIndex("idx_notification_commit_id", schemas.IndexType)
|
||||
commitIDIndex.AddColumn("commit_id")
|
||||
indices = append(indices, commitIDIndex)
|
||||
|
||||
updatedByIndex := schemas.NewIndex("idx_notification_updated_by", schemas.IndexType)
|
||||
updatedByIndex.AddColumn("updated_by")
|
||||
indices = append(indices, updatedByIndex)
|
||||
|
||||
return indices
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
|
@ -337,8 +338,10 @@ func newlyCreatedIssues(ctx context.Context, repoID int64, fromTime time.Time) *
|
|||
func activeIssues(ctx context.Context, repoID int64, fromTime time.Time) *xorm.Session {
|
||||
sess := db.GetEngine(ctx).Where("issue.repo_id = ?", repoID).
|
||||
And("issue.is_pull = ?", false).
|
||||
And("issue.created_unix >= ?", fromTime.Unix()).
|
||||
Or("issue.closed_unix >= ?", fromTime.Unix())
|
||||
And(builder.Or(
|
||||
builder.Gte{"issue.created_unix": fromTime.Unix()},
|
||||
builder.Gte{"issue.closed_unix": fromTime.Unix()},
|
||||
))
|
||||
|
||||
return sess
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ func getUserHeatmapData(ctx context.Context, user *user_model.User, team *organi
|
|||
groupByName = groupBy
|
||||
}
|
||||
|
||||
cond, err := activityQueryCondition(ctx, GetFeedsOptions{
|
||||
cond, err := ActivityQueryCondition(ctx, GetFeedsOptions{
|
||||
RequestedUser: user,
|
||||
RequestedTeam: team,
|
||||
Actor: doer,
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
package activities_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -65,11 +64,9 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
|
|||
for _, tc := range testCases {
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: tc.userID})
|
||||
|
||||
doer := &user_model.User{ID: tc.doerID}
|
||||
_, err := unittest.LoadBeanIfExists(doer)
|
||||
assert.NoError(t, err)
|
||||
if tc.doerID == 0 {
|
||||
doer = nil
|
||||
var doer *user_model.User
|
||||
if tc.doerID != 0 {
|
||||
doer = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: tc.doerID})
|
||||
}
|
||||
|
||||
// get the action for comparison
|
||||
|
@ -91,11 +88,11 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Len(t, actions, contributions, "invalid action count: did the test data became too old?")
|
||||
assert.Equal(t, count, int64(contributions))
|
||||
assert.Equal(t, tc.CountResult, contributions, fmt.Sprintf("testcase '%s'", tc.desc))
|
||||
assert.Equal(t, tc.CountResult, contributions, "testcase '%s'", tc.desc)
|
||||
|
||||
// Test JSON rendering
|
||||
jsonData, err := json.Marshal(heatmap)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.JSONResult, string(jsonData))
|
||||
assert.JSONEq(t, tc.JSONResult, string(jsonData))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ func init() {
|
|||
// TranslatableMessage represents JSON struct that can be translated with a Locale
|
||||
type TranslatableMessage struct {
|
||||
Format string
|
||||
Args []any `json:"omitempty"`
|
||||
Args []any `json:",omitempty"`
|
||||
}
|
||||
|
||||
// LoadRepo loads repository of the task
|
||||
|
|
|
@ -25,7 +25,7 @@ func (err ErrKeyUnableVerify) Error() string {
|
|||
}
|
||||
|
||||
// ErrKeyIsPrivate is returned when the provided key is a private key not a public key
|
||||
var ErrKeyIsPrivate = util.NewSilentWrapErrorf(util.ErrInvalidArgument, "the provided key is a private key")
|
||||
var ErrKeyIsPrivate = util.ErrorWrap(util.ErrInvalidArgument, "the provided key is a private key")
|
||||
|
||||
// ErrKeyNotExist represents a "KeyNotExist" kind of error.
|
||||
type ErrKeyNotExist struct {
|
||||
|
@ -217,6 +217,7 @@ func (err ErrGPGKeyAccessDenied) Unwrap() error {
|
|||
// ErrKeyAccessDenied represents a "KeyAccessDenied" kind of error.
|
||||
type ErrKeyAccessDenied struct {
|
||||
UserID int64
|
||||
RepoID int64
|
||||
KeyID int64
|
||||
Note string
|
||||
}
|
||||
|
@ -228,8 +229,8 @@ func IsErrKeyAccessDenied(err error) bool {
|
|||
}
|
||||
|
||||
func (err ErrKeyAccessDenied) Error() string {
|
||||
return fmt.Sprintf("user does not have access to the key [user_id: %d, key_id: %d, note: %s]",
|
||||
err.UserID, err.KeyID, err.Note)
|
||||
return fmt.Sprintf("user does not have access to the key [user_id: %d, repo_id: %d, key_id: %d, note: %s]",
|
||||
err.UserID, err.RepoID, err.KeyID, err.Note)
|
||||
}
|
||||
|
||||
func (err ErrKeyAccessDenied) Unwrap() error {
|
||||
|
|
|
@ -13,8 +13,8 @@ import (
|
|||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/keybase/go-crypto/openpgp"
|
||||
"github.com/keybase/go-crypto/openpgp/packet"
|
||||
"github.com/ProtonMail/go-crypto/openpgp"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
|
@ -106,7 +106,7 @@ func GPGKeyToEntity(ctx context.Context, k *GPGKey) (*openpgp.Entity, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keys, err := checkArmoredGPGKeyString(impKey.Content)
|
||||
keys, err := CheckArmoredGPGKeyString(impKey.Content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ func GPGKeyToEntity(ctx context.Context, k *GPGKey) (*openpgp.Entity, error) {
|
|||
|
||||
// parseSubGPGKey parse a sub Key
|
||||
func parseSubGPGKey(ownerID int64, primaryID string, pubkey *packet.PublicKey, expiry time.Time) (*GPGKey, error) {
|
||||
content, err := base64EncPubKey(pubkey)
|
||||
content, err := Base64EncPubKey(pubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -141,7 +141,11 @@ func parseGPGKey(ctx context.Context, ownerID int64, e *openpgp.Entity, verified
|
|||
// Parse Subkeys
|
||||
subkeys := make([]*GPGKey, len(e.Subkeys))
|
||||
for i, k := range e.Subkeys {
|
||||
subs, err := parseSubGPGKey(ownerID, pubkey.KeyIdString(), k.PublicKey, expiry)
|
||||
subkeyExpiry := expiry
|
||||
if k.Sig.KeyLifetimeSecs != nil {
|
||||
subkeyExpiry = k.PublicKey.CreationTime.Add(time.Duration(*k.Sig.KeyLifetimeSecs) * time.Second)
|
||||
}
|
||||
subs, err := parseSubGPGKey(ownerID, pubkey.KeyIdString(), k.PublicKey, subkeyExpiry)
|
||||
if err != nil {
|
||||
return nil, ErrGPGKeyParsing{ParseError: err}
|
||||
}
|
||||
|
@ -156,7 +160,7 @@ func parseGPGKey(ctx context.Context, ownerID int64, e *openpgp.Entity, verified
|
|||
|
||||
emails := make([]*user_model.EmailAddress, 0, len(e.Identities))
|
||||
for _, ident := range e.Identities {
|
||||
if ident.Revocation != nil {
|
||||
if ident.Revoked(time.Now()) {
|
||||
continue
|
||||
}
|
||||
email := strings.ToLower(strings.TrimSpace(ident.UserId.Email))
|
||||
|
@ -179,7 +183,7 @@ func parseGPGKey(ctx context.Context, ownerID int64, e *openpgp.Entity, verified
|
|||
}
|
||||
}
|
||||
|
||||
content, err := base64EncPubKey(pubkey)
|
||||
content, err := Base64EncPubKey(pubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -235,33 +239,3 @@ func DeleteGPGKey(ctx context.Context, doer *user_model.User, id int64) (err err
|
|||
|
||||
return committer.Commit()
|
||||
}
|
||||
|
||||
func checkKeyEmails(ctx context.Context, email string, keys ...*GPGKey) (bool, string) {
|
||||
uid := int64(0)
|
||||
var userEmails []*user_model.EmailAddress
|
||||
var user *user_model.User
|
||||
for _, key := range keys {
|
||||
for _, e := range key.Emails {
|
||||
if e.IsActivated && (email == "" || strings.EqualFold(e.Email, email)) {
|
||||
return true, e.Email
|
||||
}
|
||||
}
|
||||
if key.Verified && key.OwnerID != 0 {
|
||||
if uid != key.OwnerID {
|
||||
userEmails, _ = user_model.GetEmailAddresses(ctx, key.OwnerID)
|
||||
uid = key.OwnerID
|
||||
user = &user_model.User{ID: uid}
|
||||
_, _ = user_model.GetUser(ctx, user)
|
||||
}
|
||||
for _, e := range userEmails {
|
||||
if e.IsActivated && (email == "" || strings.EqualFold(e.Email, email)) {
|
||||
return true, e.Email
|
||||
}
|
||||
}
|
||||
if user.KeepEmailPrivate && strings.EqualFold(email, user.GetEmail()) {
|
||||
return true, user.GetEmail()
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, email
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"github.com/keybase/go-crypto/openpgp"
|
||||
"github.com/ProtonMail/go-crypto/openpgp"
|
||||
)
|
||||
|
||||
// __________________ ________ ____ __.
|
||||
|
@ -67,7 +67,7 @@ func addGPGSubKey(ctx context.Context, key *GPGKey) (err error) {
|
|||
|
||||
// AddGPGKey adds new public key to database.
|
||||
func AddGPGKey(ctx context.Context, ownerID int64, content, token, signature string) ([]*GPGKey, error) {
|
||||
ekeys, err := checkArmoredGPGKeyString(content)
|
||||
ekeys, err := CheckArmoredGPGKeyString(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -83,12 +83,12 @@ func AddGPGKey(ctx context.Context, ownerID int64, content, token, signature str
|
|||
verified := false
|
||||
// Handle provided signature
|
||||
if signature != "" {
|
||||
signer, err := openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token), strings.NewReader(signature))
|
||||
signer, err := openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token), strings.NewReader(signature), nil)
|
||||
if err != nil {
|
||||
signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\n"), strings.NewReader(signature))
|
||||
signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\n"), strings.NewReader(signature), nil)
|
||||
}
|
||||
if err != nil {
|
||||
signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\r\n"), strings.NewReader(signature))
|
||||
signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\r\n"), strings.NewReader(signature), nil)
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("Unable to validate token signature. Error: %v", err)
|
||||
|
|
|
@ -4,19 +4,14 @@
|
|||
package asymkey
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"hash"
|
||||
"strings"
|
||||
|
||||
"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/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/keybase/go-crypto/openpgp/packet"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||
)
|
||||
|
||||
// __________________ ________ ____ __.
|
||||
|
@ -70,263 +65,6 @@ const (
|
|||
NoKeyFound = "gpg.error.no_gpg_keys_found"
|
||||
)
|
||||
|
||||
// ParseCommitsWithSignature checks if signaute of commits are corresponding to users gpg keys.
|
||||
func ParseCommitsWithSignature(ctx context.Context, oldCommits []*user_model.UserCommit, repoTrustModel repo_model.TrustModelType, isOwnerMemberCollaborator func(*user_model.User) (bool, error)) []*SignCommit {
|
||||
newCommits := make([]*SignCommit, 0, len(oldCommits))
|
||||
keyMap := map[string]bool{}
|
||||
|
||||
for _, c := range oldCommits {
|
||||
signCommit := &SignCommit{
|
||||
UserCommit: c,
|
||||
Verification: ParseCommitWithSignature(ctx, c.Commit),
|
||||
}
|
||||
|
||||
_ = CalculateTrustStatus(signCommit.Verification, repoTrustModel, isOwnerMemberCollaborator, &keyMap)
|
||||
|
||||
newCommits = append(newCommits, signCommit)
|
||||
}
|
||||
return newCommits
|
||||
}
|
||||
|
||||
// ParseCommitWithSignature check if signature is good against keystore.
|
||||
func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *CommitVerification {
|
||||
var committer *user_model.User
|
||||
if c.Committer != nil {
|
||||
var err error
|
||||
// Find Committer account
|
||||
committer, err = user_model.GetUserByEmail(ctx, c.Committer.Email) // This finds the user by primary email or activated email so commit will not be valid if email is not
|
||||
if err != nil { // Skipping not user for committer
|
||||
committer = &user_model.User{
|
||||
Name: c.Committer.Name,
|
||||
Email: c.Committer.Email,
|
||||
}
|
||||
// We can expect this to often be an ErrUserNotExist. in the case
|
||||
// it is not, however, it is important to log it.
|
||||
if !user_model.IsErrUserNotExist(err) {
|
||||
log.Error("GetUserByEmail: %v", err)
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.no_committer_account",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no signature just report the committer
|
||||
if c.Signature == nil {
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false, // Default value
|
||||
Reason: "gpg.error.not_signed_commit", // Default value
|
||||
}
|
||||
}
|
||||
|
||||
// If this a SSH signature handle it differently
|
||||
if strings.HasPrefix(c.Signature.Signature, "-----BEGIN SSH SIGNATURE-----") {
|
||||
return ParseCommitWithSSHSignature(ctx, c, committer)
|
||||
}
|
||||
|
||||
// Parsing signature
|
||||
sig, err := extractSignature(c.Signature.Signature)
|
||||
if err != nil { // Skipping failed to extract sign
|
||||
log.Error("SignatureRead err: %v", err)
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.extract_sign",
|
||||
}
|
||||
}
|
||||
|
||||
keyID := tryGetKeyIDFromSignature(sig)
|
||||
defaultReason := NoKeyFound
|
||||
|
||||
// First check if the sig has a keyID and if so just look at that
|
||||
if commitVerification := hashAndVerifyForKeyID(
|
||||
ctx,
|
||||
sig,
|
||||
c.Signature.Payload,
|
||||
committer,
|
||||
keyID,
|
||||
setting.AppName,
|
||||
""); commitVerification != nil {
|
||||
if commitVerification.Reason == BadSignature {
|
||||
defaultReason = BadSignature
|
||||
} else {
|
||||
return commitVerification
|
||||
}
|
||||
}
|
||||
|
||||
// Now try to associate the signature with the committer, if present
|
||||
if committer.ID != 0 {
|
||||
keys, err := db.Find[GPGKey](ctx, FindGPGKeyOptions{
|
||||
OwnerID: committer.ID,
|
||||
})
|
||||
if err != nil { // Skipping failed to get gpg keys of user
|
||||
log.Error("ListGPGKeys: %v", err)
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.failed_retrieval_gpg_keys",
|
||||
}
|
||||
}
|
||||
|
||||
if err := GPGKeyList(keys).LoadSubKeys(ctx); err != nil {
|
||||
log.Error("LoadSubKeys: %v", err)
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.failed_retrieval_gpg_keys",
|
||||
}
|
||||
}
|
||||
|
||||
committerEmailAddresses, _ := user_model.GetEmailAddresses(ctx, committer.ID)
|
||||
activated := false
|
||||
for _, e := range committerEmailAddresses {
|
||||
if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) {
|
||||
activated = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, k := range keys {
|
||||
// Pre-check (& optimization) that emails attached to key can be attached to the committer email and can validate
|
||||
canValidate := false
|
||||
email := ""
|
||||
if k.Verified && activated {
|
||||
canValidate = true
|
||||
email = c.Committer.Email
|
||||
}
|
||||
if !canValidate {
|
||||
for _, e := range k.Emails {
|
||||
if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) {
|
||||
canValidate = true
|
||||
email = e.Email
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !canValidate {
|
||||
continue // Skip this key
|
||||
}
|
||||
|
||||
commitVerification := hashAndVerifyWithSubKeysCommitVerification(sig, c.Signature.Payload, k, committer, committer, email)
|
||||
if commitVerification != nil {
|
||||
return commitVerification
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if setting.Repository.Signing.SigningKey != "" && setting.Repository.Signing.SigningKey != "default" && setting.Repository.Signing.SigningKey != "none" {
|
||||
// OK we should try the default key
|
||||
gpgSettings := git.GPGSettings{
|
||||
Sign: true,
|
||||
KeyID: setting.Repository.Signing.SigningKey,
|
||||
Name: setting.Repository.Signing.SigningName,
|
||||
Email: setting.Repository.Signing.SigningEmail,
|
||||
}
|
||||
if err := gpgSettings.LoadPublicKeyContent(); err != nil {
|
||||
log.Error("Error getting default signing key: %s %v", gpgSettings.KeyID, err)
|
||||
} else if commitVerification := verifyWithGPGSettings(ctx, &gpgSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil {
|
||||
if commitVerification.Reason == BadSignature {
|
||||
defaultReason = BadSignature
|
||||
} else {
|
||||
return commitVerification
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defaultGPGSettings, err := c.GetRepositoryDefaultPublicGPGKey(false)
|
||||
if err != nil {
|
||||
log.Error("Error getting default public gpg key: %v", err)
|
||||
} else if defaultGPGSettings == nil {
|
||||
log.Warn("Unable to get defaultGPGSettings for unattached commit: %s", c.ID.String())
|
||||
} else if defaultGPGSettings.Sign {
|
||||
if commitVerification := verifyWithGPGSettings(ctx, defaultGPGSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil {
|
||||
if commitVerification.Reason == BadSignature {
|
||||
defaultReason = BadSignature
|
||||
} else {
|
||||
return commitVerification
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &CommitVerification{ // Default at this stage
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Warning: defaultReason != NoKeyFound,
|
||||
Reason: defaultReason,
|
||||
SigningKey: &GPGKey{
|
||||
KeyID: keyID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func verifyWithGPGSettings(ctx context.Context, gpgSettings *git.GPGSettings, sig *packet.Signature, payload string, committer *user_model.User, keyID string) *CommitVerification {
|
||||
// First try to find the key in the db
|
||||
if commitVerification := hashAndVerifyForKeyID(ctx, sig, payload, committer, gpgSettings.KeyID, gpgSettings.Name, gpgSettings.Email); commitVerification != nil {
|
||||
return commitVerification
|
||||
}
|
||||
|
||||
// Otherwise we have to parse the key
|
||||
ekeys, err := checkArmoredGPGKeyString(gpgSettings.PublicKeyContent)
|
||||
if err != nil {
|
||||
log.Error("Unable to get default signing key: %v", err)
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.generate_hash",
|
||||
}
|
||||
}
|
||||
for _, ekey := range ekeys {
|
||||
pubkey := ekey.PrimaryKey
|
||||
content, err := base64EncPubKey(pubkey)
|
||||
if err != nil {
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.generate_hash",
|
||||
}
|
||||
}
|
||||
k := &GPGKey{
|
||||
Content: content,
|
||||
CanSign: pubkey.CanSign(),
|
||||
KeyID: pubkey.KeyIdString(),
|
||||
}
|
||||
for _, subKey := range ekey.Subkeys {
|
||||
content, err := base64EncPubKey(subKey.PublicKey)
|
||||
if err != nil {
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.generate_hash",
|
||||
}
|
||||
}
|
||||
k.SubsKey = append(k.SubsKey, &GPGKey{
|
||||
Content: content,
|
||||
CanSign: subKey.PublicKey.CanSign(),
|
||||
KeyID: subKey.PublicKey.KeyIdString(),
|
||||
})
|
||||
}
|
||||
if commitVerification := hashAndVerifyWithSubKeysCommitVerification(sig, payload, k, committer, &user_model.User{
|
||||
Name: gpgSettings.Name,
|
||||
Email: gpgSettings.Email,
|
||||
}, gpgSettings.Email); commitVerification != nil {
|
||||
return commitVerification
|
||||
}
|
||||
if keyID == k.KeyID {
|
||||
// This is a bad situation ... We have a key id that matches our default key but the signature doesn't match.
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Warning: true,
|
||||
Reason: BadSignature,
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifySign(s *packet.Signature, h hash.Hash, k *GPGKey) error {
|
||||
// Check if key can sign
|
||||
if !k.CanSign {
|
||||
|
@ -369,7 +107,7 @@ func hashAndVerifyWithSubKeys(sig *packet.Signature, payload string, k *GPGKey)
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func hashAndVerifyWithSubKeysCommitVerification(sig *packet.Signature, payload string, k *GPGKey, committer, signer *user_model.User, email string) *CommitVerification {
|
||||
func HashAndVerifyWithSubKeysCommitVerification(sig *packet.Signature, payload string, k *GPGKey, committer, signer *user_model.User, email string) *CommitVerification {
|
||||
key, err := hashAndVerifyWithSubKeys(sig, payload, k)
|
||||
if err != nil { // Skipping failed to generate hash
|
||||
return &CommitVerification{
|
||||
|
@ -392,78 +130,6 @@ func hashAndVerifyWithSubKeysCommitVerification(sig *packet.Signature, payload s
|
|||
return nil
|
||||
}
|
||||
|
||||
func hashAndVerifyForKeyID(ctx context.Context, sig *packet.Signature, payload string, committer *user_model.User, keyID, name, email string) *CommitVerification {
|
||||
if keyID == "" {
|
||||
return nil
|
||||
}
|
||||
keys, err := db.Find[GPGKey](ctx, FindGPGKeyOptions{
|
||||
KeyID: keyID,
|
||||
IncludeSubKeys: true,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("GetGPGKeysByKeyID: %v", err)
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.failed_retrieval_gpg_keys",
|
||||
}
|
||||
}
|
||||
if len(keys) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, key := range keys {
|
||||
var primaryKeys []*GPGKey
|
||||
if key.PrimaryKeyID != "" {
|
||||
primaryKeys, err = db.Find[GPGKey](ctx, FindGPGKeyOptions{
|
||||
KeyID: key.PrimaryKeyID,
|
||||
IncludeSubKeys: true,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("GetGPGKeysByKeyID: %v", err)
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.failed_retrieval_gpg_keys",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
activated, email := checkKeyEmails(ctx, email, append([]*GPGKey{key}, primaryKeys...)...)
|
||||
if !activated {
|
||||
continue
|
||||
}
|
||||
|
||||
signer := &user_model.User{
|
||||
Name: name,
|
||||
Email: email,
|
||||
}
|
||||
if key.OwnerID != 0 {
|
||||
owner, err := user_model.GetUserByID(ctx, key.OwnerID)
|
||||
if err == nil {
|
||||
signer = owner
|
||||
} else if !user_model.IsErrUserNotExist(err) {
|
||||
log.Error("Failed to user_model.GetUserByID: %d for key ID: %d (%s) %v", key.OwnerID, key.ID, key.KeyID, err)
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Reason: "gpg.error.no_committer_account",
|
||||
}
|
||||
}
|
||||
}
|
||||
commitVerification := hashAndVerifyWithSubKeysCommitVerification(sig, payload, key, committer, signer, email)
|
||||
if commitVerification != nil {
|
||||
return commitVerification
|
||||
}
|
||||
}
|
||||
// This is a bad situation ... We have a key id that is in our database but the signature doesn't match.
|
||||
return &CommitVerification{
|
||||
CommittingUser: committer,
|
||||
Verified: false,
|
||||
Warning: true,
|
||||
Reason: BadSignature,
|
||||
}
|
||||
}
|
||||
|
||||
// CalculateTrustStatus will calculate the TrustStatus for a commit verification within a repository
|
||||
// There are several trust models in Gitea
|
||||
func CalculateTrustStatus(verification *CommitVerification, repoTrustModel repo_model.TrustModelType, isOwnerMemberCollaborator func(*user_model.User) (bool, error), keyMap *map[string]bool) error {
|
||||
|
|
|
@ -13,9 +13,9 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/keybase/go-crypto/openpgp"
|
||||
"github.com/keybase/go-crypto/openpgp/armor"
|
||||
"github.com/keybase/go-crypto/openpgp/packet"
|
||||
"github.com/ProtonMail/go-crypto/openpgp"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/armor"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||
)
|
||||
|
||||
// __________________ ________ ____ __.
|
||||
|
@ -33,9 +33,9 @@ import (
|
|||
|
||||
// This file provides common functions relating to GPG Keys
|
||||
|
||||
// checkArmoredGPGKeyString checks if the given key string is a valid GPG armored key.
|
||||
// CheckArmoredGPGKeyString checks if the given key string is a valid GPG armored key.
|
||||
// The function returns the actual public key on success
|
||||
func checkArmoredGPGKeyString(content string) (openpgp.EntityList, error) {
|
||||
func CheckArmoredGPGKeyString(content string) (openpgp.EntityList, error) {
|
||||
list, err := openpgp.ReadArmoredKeyRing(strings.NewReader(content))
|
||||
if err != nil {
|
||||
return nil, ErrGPGKeyParsing{err}
|
||||
|
@ -43,8 +43,8 @@ func checkArmoredGPGKeyString(content string) (openpgp.EntityList, error) {
|
|||
return list, nil
|
||||
}
|
||||
|
||||
// base64EncPubKey encode public key content to base 64
|
||||
func base64EncPubKey(pubkey *packet.PublicKey) (string, error) {
|
||||
// Base64EncPubKey encode public key content to base 64
|
||||
func Base64EncPubKey(pubkey *packet.PublicKey) (string, error) {
|
||||
var w bytes.Buffer
|
||||
err := pubkey.Serialize(&w)
|
||||
if err != nil {
|
||||
|
@ -80,7 +80,7 @@ func base64DecPubKey(content string) (*packet.PublicKey, error) {
|
|||
return pkey, nil
|
||||
}
|
||||
|
||||
// getExpiryTime extract the expire time of primary key based on sig
|
||||
// getExpiryTime extract the expiry time of primary key based on sig
|
||||
func getExpiryTime(e *openpgp.Entity) time.Time {
|
||||
expiry := time.Time{}
|
||||
// Extract self-sign for expire date based on : https://github.com/golang/crypto/blob/master/openpgp/keys.go#L165
|
||||
|
@ -88,12 +88,12 @@ func getExpiryTime(e *openpgp.Entity) time.Time {
|
|||
for _, ident := range e.Identities {
|
||||
if selfSig == nil {
|
||||
selfSig = ident.SelfSignature
|
||||
} else if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
|
||||
} else if ident.SelfSignature != nil && ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
|
||||
selfSig = ident.SelfSignature
|
||||
break
|
||||
}
|
||||
}
|
||||
if selfSig.KeyLifetimeSecs != nil {
|
||||
if selfSig != nil && selfSig.KeyLifetimeSecs != nil {
|
||||
expiry = e.PrimaryKey.CreationTime.Add(time.Duration(*selfSig.KeyLifetimeSecs) * time.Second)
|
||||
}
|
||||
return expiry
|
||||
|
@ -119,7 +119,7 @@ func readArmoredSign(r io.Reader) (body io.Reader, err error) {
|
|||
return block.Body, nil
|
||||
}
|
||||
|
||||
func extractSignature(s string) (*packet.Signature, error) {
|
||||
func ExtractSignature(s string) (*packet.Signature, error) {
|
||||
r, err := readArmoredSign(strings.NewReader(s))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to read signature armor")
|
||||
|
@ -135,7 +135,7 @@ func extractSignature(s string) (*packet.Signature, error) {
|
|||
return sig, nil
|
||||
}
|
||||
|
||||
func tryGetKeyIDFromSignature(sig *packet.Signature) string {
|
||||
func TryGetKeyIDFromSignature(sig *packet.Signature) string {
|
||||
if sig.IssuerKeyId != nil && (*sig.IssuerKeyId) != 0 {
|
||||
return fmt.Sprintf("%016X", *sig.IssuerKeyId)
|
||||
}
|
||||
|
|
|
@ -13,8 +13,10 @@ import (
|
|||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/keybase/go-crypto/openpgp/packet"
|
||||
"github.com/ProtonMail/go-crypto/openpgp"
|
||||
"github.com/ProtonMail/go-crypto/openpgp/packet"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCheckArmoredGPGKeyString(t *testing.T) {
|
||||
|
@ -49,7 +51,7 @@ MkM/fdpyc2hY7Dl/+qFmN5MG5yGmMpQcX+RNNR222ibNC1D3wg==
|
|||
=i9b7
|
||||
-----END PGP PUBLIC KEY BLOCK-----`
|
||||
|
||||
key, err := checkArmoredGPGKeyString(testGPGArmor)
|
||||
key, err := CheckArmoredGPGKeyString(testGPGArmor)
|
||||
assert.NoError(t, err, "Could not parse a valid GPG public armored rsa key", key)
|
||||
// TODO verify value of key
|
||||
}
|
||||
|
@ -70,7 +72,7 @@ OyjLLnFQiVmq7kEA/0z0CQe3ZQiQIq5zrs7Nh1XRkFAo8GlU/SGC9XFFi722
|
|||
=ZiSe
|
||||
-----END PGP PUBLIC KEY BLOCK-----`
|
||||
|
||||
key, err := checkArmoredGPGKeyString(testGPGArmor)
|
||||
key, err := CheckArmoredGPGKeyString(testGPGArmor)
|
||||
assert.NoError(t, err, "Could not parse a valid GPG public armored brainpoolP256r1 key", key)
|
||||
// TODO verify value of key
|
||||
}
|
||||
|
@ -106,15 +108,14 @@ Av844q/BfRuVsJsK1NDNG09LC30B0l3LKBqlrRmRTUMHtgchdX2dY+p7GPOoSzlR
|
|||
MkM/fdpyc2hY7Dl/+qFmN5MG5yGmMpQcX+RNNR222ibNC1D3wg==
|
||||
=i9b7
|
||||
-----END PGP PUBLIC KEY BLOCK-----`
|
||||
keys, err := checkArmoredGPGKeyString(testGPGArmor)
|
||||
if !assert.NotEmpty(t, keys) {
|
||||
return
|
||||
}
|
||||
keys, err := CheckArmoredGPGKeyString(testGPGArmor)
|
||||
require.NotEmpty(t, keys)
|
||||
|
||||
ekey := keys[0]
|
||||
assert.NoError(t, err, "Could not parse a valid GPG armored key", ekey)
|
||||
|
||||
pubkey := ekey.PrimaryKey
|
||||
content, err := base64EncPubKey(pubkey)
|
||||
content, err := Base64EncPubKey(pubkey)
|
||||
assert.NoError(t, err, "Could not base64 encode a valid PublicKey content", ekey)
|
||||
|
||||
key := &GPGKey{
|
||||
|
@ -175,9 +176,9 @@ committer Antoine GIRARD <sapk@sapk.fr> 1489013107 +0100
|
|||
Unknown GPG key with good email
|
||||
`
|
||||
// Reading Sign
|
||||
goodSig, err := extractSignature(testGoodSigArmor)
|
||||
goodSig, err := ExtractSignature(testGoodSigArmor)
|
||||
assert.NoError(t, err, "Could not parse a valid GPG armored signature", testGoodSigArmor)
|
||||
badSig, err := extractSignature(testBadSigArmor)
|
||||
badSig, err := ExtractSignature(testBadSigArmor)
|
||||
assert.NoError(t, err, "Could not parse a valid GPG armored signature", testBadSigArmor)
|
||||
|
||||
// Generating hash of commit
|
||||
|
@ -385,7 +386,7 @@ epiDVQ==
|
|||
=VSKJ
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
`
|
||||
keys, err := checkArmoredGPGKeyString(testIssue6599)
|
||||
keys, err := CheckArmoredGPGKeyString(testIssue6599)
|
||||
assert.NoError(t, err)
|
||||
if assert.NotEmpty(t, keys) {
|
||||
ekey := keys[0]
|
||||
|
@ -395,11 +396,33 @@ epiDVQ==
|
|||
}
|
||||
|
||||
func TestTryGetKeyIDFromSignature(t *testing.T) {
|
||||
assert.Empty(t, tryGetKeyIDFromSignature(&packet.Signature{}))
|
||||
assert.Equal(t, "038D1A3EADDBEA9C", tryGetKeyIDFromSignature(&packet.Signature{
|
||||
assert.Empty(t, TryGetKeyIDFromSignature(&packet.Signature{}))
|
||||
assert.Equal(t, "038D1A3EADDBEA9C", TryGetKeyIDFromSignature(&packet.Signature{
|
||||
IssuerKeyId: util.ToPointer(uint64(0x38D1A3EADDBEA9C)),
|
||||
}))
|
||||
assert.Equal(t, "038D1A3EADDBEA9C", tryGetKeyIDFromSignature(&packet.Signature{
|
||||
assert.Equal(t, "038D1A3EADDBEA9C", TryGetKeyIDFromSignature(&packet.Signature{
|
||||
IssuerFingerprint: []uint8{0xb, 0x23, 0x24, 0xc7, 0xe6, 0xfe, 0x4f, 0x3a, 0x6, 0x26, 0xc1, 0x21, 0x3, 0x8d, 0x1a, 0x3e, 0xad, 0xdb, 0xea, 0x9c},
|
||||
}))
|
||||
}
|
||||
|
||||
func TestParseGPGKey(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
assert.NoError(t, db.Insert(db.DefaultContext, &user_model.EmailAddress{UID: 1, Email: "email1@example.com", IsActivated: true}))
|
||||
|
||||
// create a key for test email
|
||||
e, err := openpgp.NewEntity("name", "comment", "email1@example.com", nil)
|
||||
require.NoError(t, err)
|
||||
k, err := parseGPGKey(db.DefaultContext, 1, e, true)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, k.KeyID)
|
||||
assert.NotEmpty(t, k.Emails) // the key is valid, matches the email
|
||||
|
||||
// then revoke the key
|
||||
for _, id := range e.Identities {
|
||||
id.Revocations = append(id.Revocations, &packet.Signature{RevocationReason: util.ToPointer(packet.KeyCompromised)})
|
||||
}
|
||||
k, err = parseGPGKey(db.DefaultContext, 1, e, true)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, k.KeyID)
|
||||
assert.Empty(t, k.Emails) // the key is revoked, matches no email
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ func VerifyGPGKey(ctx context.Context, ownerID int64, keyID, token, signature st
|
|||
return "", err
|
||||
}
|
||||
|
||||
sig, err := extractSignature(signature)
|
||||
sig, err := ExtractSignature(signature)
|
||||
if err != nil {
|
||||
return "", ErrGPGInvalidTokenSignature{
|
||||
ID: key.KeyID,
|
||||
|
|
|
@ -5,6 +5,7 @@ package auth
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
|
@ -14,7 +15,7 @@ import (
|
|||
type AccessTokenScopeCategory int
|
||||
|
||||
const (
|
||||
AccessTokenScopeCategoryActivityPub = iota
|
||||
AccessTokenScopeCategoryActivityPub AccessTokenScopeCategory = iota
|
||||
AccessTokenScopeCategoryAdmin
|
||||
AccessTokenScopeCategoryMisc // WARN: this is now just a placeholder, don't remove it which will change the following values
|
||||
AccessTokenScopeCategoryNotification
|
||||
|
@ -193,6 +194,14 @@ var accessTokenScopes = map[AccessTokenScopeLevel]map[AccessTokenScopeCategory]A
|
|||
},
|
||||
}
|
||||
|
||||
func GetAccessTokenCategories() (res []string) {
|
||||
for _, cat := range accessTokenScopes[Read] {
|
||||
res = append(res, strings.TrimPrefix(string(cat), "read:"))
|
||||
}
|
||||
slices.Sort(res)
|
||||
return res
|
||||
}
|
||||
|
||||
// GetRequiredScopes gets the specific scopes for a given level and categories
|
||||
func GetRequiredScopes(level AccessTokenScopeLevel, scopeCategories ...AccessTokenScopeCategory) []AccessTokenScope {
|
||||
scopes := make([]AccessTokenScope, 0, len(scopeCategories))
|
||||
|
@ -270,6 +279,9 @@ func (s AccessTokenScope) parse() (accessTokenScopeBitmap, error) {
|
|||
|
||||
// StringSlice returns the AccessTokenScope as a []string
|
||||
func (s AccessTokenScope) StringSlice() []string {
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
return strings.Split(string(s), ",")
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ type scopeTestNormalize struct {
|
|||
}
|
||||
|
||||
func TestAccessTokenScope_Normalize(t *testing.T) {
|
||||
assert.Equal(t, []string{"activitypub", "admin", "issue", "misc", "notification", "organization", "package", "repository", "user"}, GetAccessTokenCategories())
|
||||
tests := []scopeTestNormalize{
|
||||
{"", "", nil},
|
||||
{"write:misc,write:notification,read:package,write:notification,public-only", "public-only,write:misc,write:notification,read:package", nil},
|
||||
|
@ -25,7 +26,7 @@ func TestAccessTokenScope_Normalize(t *testing.T) {
|
|||
{"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user,public-only", "public-only,all", nil},
|
||||
}
|
||||
|
||||
for _, scope := range []string{"activitypub", "admin", "misc", "notification", "organization", "package", "issue", "repository", "user"} {
|
||||
for _, scope := range GetAccessTokenCategories() {
|
||||
tests = append(tests,
|
||||
scopeTestNormalize{AccessTokenScope(fmt.Sprintf("read:%s", scope)), AccessTokenScope(fmt.Sprintf("read:%s", scope)), nil},
|
||||
scopeTestNormalize{AccessTokenScope(fmt.Sprintf("write:%s", scope)), AccessTokenScope(fmt.Sprintf("write:%s", scope)), nil},
|
||||
|
@ -59,7 +60,7 @@ func TestAccessTokenScope_HasScope(t *testing.T) {
|
|||
{"public-only", "read:issue", false, nil},
|
||||
}
|
||||
|
||||
for _, scope := range []string{"activitypub", "admin", "misc", "notification", "organization", "package", "issue", "repository", "user"} {
|
||||
for _, scope := range GetAccessTokenCategories() {
|
||||
tests = append(tests,
|
||||
scopeTestHasScope{
|
||||
AccessTokenScope(fmt.Sprintf("read:%s", scope)),
|
||||
|
|
|
@ -18,14 +18,14 @@ func TestOAuth2Application_GenerateClientSecret(t *testing.T) {
|
|||
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
|
||||
secret, err := app.GenerateClientSecret(db.DefaultContext)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, len(secret) > 0)
|
||||
assert.NotEmpty(t, secret)
|
||||
unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1, ClientSecret: app.ClientSecret})
|
||||
}
|
||||
|
||||
func BenchmarkOAuth2Application_GenerateClientSecret(b *testing.B) {
|
||||
assert.NoError(b, unittest.PrepareTestDatabase())
|
||||
app := unittest.AssertExistsAndLoadBean(b, &auth_model.OAuth2Application{ID: 1})
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
_, _ = app.GenerateClientSecret(db.DefaultContext)
|
||||
}
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ func TestOAuth2Grant_GenerateNewAuthorizationCode(t *testing.T) {
|
|||
code, err := grant.GenerateNewAuthorizationCode(db.DefaultContext, "https://example2.com/callback", "CjvyTLSdR47G5zYenDA-eDWW4lRrO8yvjcWwbD_deOg", "S256")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, code)
|
||||
assert.True(t, len(code.Code) > 32) // secret length > 32
|
||||
assert.Greater(t, len(code.Code), 32) // secret length > 32
|
||||
}
|
||||
|
||||
func TestOAuth2Grant_TableName(t *testing.T) {
|
||||
|
|
|
@ -13,6 +13,8 @@ import (
|
|||
"code.gitea.io/gitea/modules/json"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/schemas"
|
||||
)
|
||||
|
||||
|
@ -54,7 +56,8 @@ func TestDumpAuthSource(t *testing.T) {
|
|||
|
||||
sb := new(strings.Builder)
|
||||
|
||||
db.DumpTables([]*schemas.Table{authSourceSchema}, sb)
|
||||
|
||||
// TODO: this test is quite hacky, it should use a low-level "select" (without model processors) but not a database dump
|
||||
engine := db.GetEngine(db.DefaultContext).(*xorm.Engine)
|
||||
require.NoError(t, engine.DumpTables([]*schemas.Table{authSourceSchema}, sb))
|
||||
assert.Contains(t, sb.String(), `"Provider":"ConvertibleSourceName"`)
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
)
|
||||
|
||||
|
@ -89,14 +90,33 @@ func (cred *WebAuthnCredential) AfterLoad() {
|
|||
// WebAuthnCredentialList is a list of *WebAuthnCredential
|
||||
type WebAuthnCredentialList []*WebAuthnCredential
|
||||
|
||||
// newCredentialFlagsFromAuthenticatorFlags is copied from https://github.com/go-webauthn/webauthn/pull/337
|
||||
// to convert protocol.AuthenticatorFlags to webauthn.CredentialFlags
|
||||
func newCredentialFlagsFromAuthenticatorFlags(flags protocol.AuthenticatorFlags) webauthn.CredentialFlags {
|
||||
return webauthn.CredentialFlags{
|
||||
UserPresent: flags.HasUserPresent(),
|
||||
UserVerified: flags.HasUserVerified(),
|
||||
BackupEligible: flags.HasBackupEligible(),
|
||||
BackupState: flags.HasBackupState(),
|
||||
}
|
||||
}
|
||||
|
||||
// ToCredentials will convert all WebAuthnCredentials to webauthn.Credentials
|
||||
func (list WebAuthnCredentialList) ToCredentials() []webauthn.Credential {
|
||||
func (list WebAuthnCredentialList) ToCredentials(defaultAuthFlags ...protocol.AuthenticatorFlags) []webauthn.Credential {
|
||||
// TODO: at the moment, Gitea doesn't store or check the flags
|
||||
// so we need to use the default flags from the authenticator to make the login validation pass
|
||||
// In the future, we should:
|
||||
// 1. store the flags when registering the credential
|
||||
// 2. provide the stored flags when converting the credentials (for login)
|
||||
// 3. for old users, still use this fallback to the default flags
|
||||
defAuthFlags := util.OptionalArg(defaultAuthFlags)
|
||||
creds := make([]webauthn.Credential, 0, len(list))
|
||||
for _, cred := range list {
|
||||
creds = append(creds, webauthn.Credential{
|
||||
ID: cred.CredentialID,
|
||||
PublicKey: cred.PublicKey,
|
||||
AttestationType: cred.AttestationType,
|
||||
Flags: newCredentialFlagsFromAuthenticatorFlags(defAuthFlags),
|
||||
Authenticator: webauthn.Authenticator{
|
||||
AAGUID: cred.AAGUID,
|
||||
SignCount: cred.SignCount,
|
||||
|
|
|
@ -44,7 +44,7 @@ func TestWebAuthnCredential_UpdateSignCount(t *testing.T) {
|
|||
cred := unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1})
|
||||
cred.SignCount = 1
|
||||
assert.NoError(t, cred.UpdateSignCount(db.DefaultContext))
|
||||
unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{ID: 1, SignCount: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1, SignCount: 1})
|
||||
}
|
||||
|
||||
func TestWebAuthnCredential_UpdateLargeCounter(t *testing.T) {
|
||||
|
@ -52,7 +52,7 @@ func TestWebAuthnCredential_UpdateLargeCounter(t *testing.T) {
|
|||
cred := unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1})
|
||||
cred.SignCount = 0xffffffff
|
||||
assert.NoError(t, cred.UpdateSignCount(db.DefaultContext))
|
||||
unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{ID: 1, SignCount: 0xffffffff})
|
||||
unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1, SignCount: 0xffffffff})
|
||||
}
|
||||
|
||||
func TestCreateCredential(t *testing.T) {
|
||||
|
@ -63,5 +63,5 @@ func TestCreateCredential(t *testing.T) {
|
|||
assert.Equal(t, "WebAuthn Created Credential", res.Name)
|
||||
assert.Equal(t, []byte("Test"), res.CredentialID)
|
||||
|
||||
unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1})
|
||||
}
|
||||
|
|
|
@ -68,7 +68,8 @@ func CheckCollations(x *xorm.Engine) (*CheckCollationsResult, error) {
|
|||
|
||||
var candidateCollations []string
|
||||
if x.Dialect().URI().DBType == schemas.MYSQL {
|
||||
if _, err = x.SQL("SELECT @@collation_database").Get(&res.DatabaseCollation); err != nil {
|
||||
_, err = x.SQL("SELECT DEFAULT_COLLATION_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?", setting.Database.Name).Get(&res.DatabaseCollation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.IsCollationCaseSensitive = func(s string) bool {
|
||||
|
@ -139,7 +140,7 @@ func CheckCollations(x *xorm.Engine) (*CheckCollationsResult, error) {
|
|||
}
|
||||
|
||||
func CheckCollationsDefaultEngine() (*CheckCollationsResult, error) {
|
||||
return CheckCollations(x)
|
||||
return CheckCollations(xormEngine)
|
||||
}
|
||||
|
||||
func alterDatabaseCollation(x *xorm.Engine, collation string) error {
|
||||
|
|
|
@ -6,6 +6,12 @@ package db
|
|||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"runtime"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
|
@ -15,45 +21,23 @@ import (
|
|||
// will be overwritten by Init with HammerContext
|
||||
var DefaultContext context.Context
|
||||
|
||||
// contextKey is a value for use with context.WithValue.
|
||||
type contextKey struct {
|
||||
name string
|
||||
}
|
||||
type engineContextKeyType struct{}
|
||||
|
||||
// enginedContextKey is a context key. It is used with context.Value() to get the current Engined for the context
|
||||
var (
|
||||
enginedContextKey = &contextKey{"engined"}
|
||||
_ Engined = &Context{}
|
||||
)
|
||||
var engineContextKey = engineContextKeyType{}
|
||||
|
||||
// Context represents a db context
|
||||
type Context struct {
|
||||
context.Context
|
||||
e Engine
|
||||
transaction bool
|
||||
engine Engine
|
||||
}
|
||||
|
||||
func newContext(ctx context.Context, e Engine, transaction bool) *Context {
|
||||
return &Context{
|
||||
Context: ctx,
|
||||
e: e,
|
||||
transaction: transaction,
|
||||
}
|
||||
}
|
||||
|
||||
// InTransaction if context is in a transaction
|
||||
func (ctx *Context) InTransaction() bool {
|
||||
return ctx.transaction
|
||||
}
|
||||
|
||||
// Engine returns db engine
|
||||
func (ctx *Context) Engine() Engine {
|
||||
return ctx.e
|
||||
func newContext(ctx context.Context, e Engine) *Context {
|
||||
return &Context{Context: ctx, engine: e}
|
||||
}
|
||||
|
||||
// Value shadows Value for context.Context but allows us to get ourselves and an Engined object
|
||||
func (ctx *Context) Value(key any) any {
|
||||
if key == enginedContextKey {
|
||||
if key == engineContextKey {
|
||||
return ctx
|
||||
}
|
||||
return ctx.Context.Value(key)
|
||||
|
@ -61,30 +45,66 @@ func (ctx *Context) Value(key any) any {
|
|||
|
||||
// WithContext returns this engine tied to this context
|
||||
func (ctx *Context) WithContext(other context.Context) *Context {
|
||||
return newContext(ctx, ctx.e.Context(other), ctx.transaction)
|
||||
return newContext(ctx, ctx.engine.Context(other))
|
||||
}
|
||||
|
||||
// Engined structs provide an Engine
|
||||
type Engined interface {
|
||||
Engine() Engine
|
||||
var (
|
||||
contextSafetyOnce sync.Once
|
||||
contextSafetyDeniedFuncPCs []uintptr
|
||||
)
|
||||
|
||||
func contextSafetyCheck(e Engine) {
|
||||
if setting.IsProd && !setting.IsInTesting {
|
||||
return
|
||||
}
|
||||
if e == nil {
|
||||
return
|
||||
}
|
||||
// Only do this check for non-end-users. If the problem could be fixed in the future, this code could be removed.
|
||||
contextSafetyOnce.Do(func() {
|
||||
// try to figure out the bad functions to deny
|
||||
type m struct{}
|
||||
_ = e.SQL("SELECT 1").Iterate(&m{}, func(int, any) error {
|
||||
callers := make([]uintptr, 32)
|
||||
callerNum := runtime.Callers(1, callers)
|
||||
for i := 0; i < callerNum; i++ {
|
||||
if funcName := runtime.FuncForPC(callers[i]).Name(); funcName == "xorm.io/xorm.(*Session).Iterate" {
|
||||
contextSafetyDeniedFuncPCs = append(contextSafetyDeniedFuncPCs, callers[i])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if len(contextSafetyDeniedFuncPCs) != 1 {
|
||||
panic(errors.New("unable to determine the functions to deny"))
|
||||
}
|
||||
})
|
||||
|
||||
// it should be very fast: xxxx ns/op
|
||||
callers := make([]uintptr, 32)
|
||||
callerNum := runtime.Callers(3, callers) // skip 3: runtime.Callers, contextSafetyCheck, GetEngine
|
||||
for i := 0; i < callerNum; i++ {
|
||||
if slices.Contains(contextSafetyDeniedFuncPCs, callers[i]) {
|
||||
panic(errors.New("using database context in an iterator would cause corrupted results"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetEngine will get a db Engine from this context or return an Engine restricted to this context
|
||||
// GetEngine gets an existing db Engine/Statement or creates a new Session
|
||||
func GetEngine(ctx context.Context) Engine {
|
||||
if e := getEngine(ctx); e != nil {
|
||||
if e := getExistingEngine(ctx); e != nil {
|
||||
return e
|
||||
}
|
||||
return x.Context(ctx)
|
||||
return xormEngine.Context(ctx)
|
||||
}
|
||||
|
||||
// getEngine will get a db Engine from this context or return nil
|
||||
func getEngine(ctx context.Context) Engine {
|
||||
if engined, ok := ctx.(Engined); ok {
|
||||
return engined.Engine()
|
||||
// getExistingEngine gets an existing db Engine/Statement from this context or returns nil
|
||||
func getExistingEngine(ctx context.Context) (e Engine) {
|
||||
defer func() { contextSafetyCheck(e) }()
|
||||
if engined, ok := ctx.(*Context); ok {
|
||||
return engined.engine
|
||||
}
|
||||
enginedInterface := ctx.Value(enginedContextKey)
|
||||
if enginedInterface != nil {
|
||||
return enginedInterface.(Engined).Engine()
|
||||
if engined, ok := ctx.Value(engineContextKey).(*Context); ok {
|
||||
return engined.engine
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -132,23 +152,23 @@ func (c *halfCommitter) Close() error {
|
|||
// d. It doesn't mean rollback is forbidden, but always do it only when there is an error, and you do want to rollback.
|
||||
func TxContext(parentCtx context.Context) (*Context, Committer, error) {
|
||||
if sess, ok := inTransaction(parentCtx); ok {
|
||||
return newContext(parentCtx, sess, true), &halfCommitter{committer: sess}, nil
|
||||
return newContext(parentCtx, sess), &halfCommitter{committer: sess}, nil
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
sess := xormEngine.NewSession()
|
||||
if err := sess.Begin(); err != nil {
|
||||
sess.Close()
|
||||
_ = sess.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return newContext(DefaultContext, sess, true), sess, nil
|
||||
return newContext(DefaultContext, sess), sess, nil
|
||||
}
|
||||
|
||||
// WithTx represents executing database operations on a transaction, if the transaction exist,
|
||||
// this function will reuse it otherwise will create a new one and close it when finished.
|
||||
func WithTx(parentCtx context.Context, f func(ctx context.Context) error) error {
|
||||
if sess, ok := inTransaction(parentCtx); ok {
|
||||
err := f(newContext(parentCtx, sess, true))
|
||||
err := f(newContext(parentCtx, sess))
|
||||
if err != nil {
|
||||
// rollback immediately, in case the caller ignores returned error and tries to commit the transaction.
|
||||
_ = sess.Close()
|
||||
|
@ -159,13 +179,13 @@ func WithTx(parentCtx context.Context, f func(ctx context.Context) error) error
|
|||
}
|
||||
|
||||
func txWithNoCheck(parentCtx context.Context, f func(ctx context.Context) error) error {
|
||||
sess := x.NewSession()
|
||||
sess := xormEngine.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := f(newContext(parentCtx, sess, true)); err != nil {
|
||||
if err := f(newContext(parentCtx, sess)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -269,6 +289,9 @@ func FindIDs(ctx context.Context, tableName, idCol string, cond builder.Cond) ([
|
|||
// DecrByIDs decreases the given column for entities of the "bean" type with one of the given ids by one
|
||||
// Timestamps of the entities won't be updated
|
||||
func DecrByIDs(ctx context.Context, ids []int64, decrCol string, bean any) error {
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
_, err := GetEngine(ctx).Decr(decrCol).In("id", ids).NoAutoCondition().NoAutoTime().Update(bean)
|
||||
return err
|
||||
}
|
||||
|
@ -302,7 +325,7 @@ func CountByBean(ctx context.Context, bean any) (int64, error) {
|
|||
|
||||
// TableName returns the table name according a bean object
|
||||
func TableName(bean any) string {
|
||||
return x.TableName(bean)
|
||||
return xormEngine.TableName(bean)
|
||||
}
|
||||
|
||||
// InTransaction returns true if the engine is in a transaction otherwise return false
|
||||
|
@ -312,7 +335,7 @@ func InTransaction(ctx context.Context) bool {
|
|||
}
|
||||
|
||||
func inTransaction(ctx context.Context) (*xorm.Session, bool) {
|
||||
e := getEngine(ctx)
|
||||
e := getExistingEngine(ctx)
|
||||
if e == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
package db // it's not db_test, because this file is for testing the private type halfCommitter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -80,7 +80,7 @@ func Test_halfCommitter(t *testing.T) {
|
|||
testWithCommitter(mockCommitter, func(committer Committer) error {
|
||||
defer committer.Close()
|
||||
if true {
|
||||
return fmt.Errorf("error")
|
||||
return errors.New("error")
|
||||
}
|
||||
return committer.Commit()
|
||||
})
|
||||
|
@ -94,7 +94,7 @@ func Test_halfCommitter(t *testing.T) {
|
|||
testWithCommitter(mockCommitter, func(committer Committer) error {
|
||||
committer.Close()
|
||||
committer.Commit()
|
||||
return fmt.Errorf("error")
|
||||
return errors.New("error")
|
||||
})
|
||||
|
||||
mockCommitter.Assert(t)
|
||||
|
|
|
@ -84,3 +84,47 @@ func TestTxContext(t *testing.T) {
|
|||
}))
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextSafety(t *testing.T) {
|
||||
type TestModel1 struct {
|
||||
ID int64
|
||||
}
|
||||
type TestModel2 struct {
|
||||
ID int64
|
||||
}
|
||||
assert.NoError(t, unittest.GetXORMEngine().Sync(&TestModel1{}, &TestModel2{}))
|
||||
assert.NoError(t, db.TruncateBeans(db.DefaultContext, &TestModel1{}, &TestModel2{}))
|
||||
testCount := 10
|
||||
for i := 1; i <= testCount; i++ {
|
||||
assert.NoError(t, db.Insert(db.DefaultContext, &TestModel1{ID: int64(i)}))
|
||||
assert.NoError(t, db.Insert(db.DefaultContext, &TestModel2{ID: int64(-i)}))
|
||||
}
|
||||
|
||||
actualCount := 0
|
||||
// here: db.GetEngine(db.DefaultContext) is a new *Session created from *Engine
|
||||
_ = db.WithTx(db.DefaultContext, func(ctx context.Context) error {
|
||||
_ = db.GetEngine(ctx).Iterate(&TestModel1{}, func(i int, bean any) error {
|
||||
// here: db.GetEngine(ctx) is always the unclosed "Iterate" *Session with autoResetStatement=false,
|
||||
// and the internal states (including "cond" and others) are always there and not be reset in this callback.
|
||||
m1 := bean.(*TestModel1)
|
||||
assert.EqualValues(t, i+1, m1.ID)
|
||||
|
||||
// here: XORM bug, it fails because the SQL becomes "WHERE id=-1", "WHERE id=-1 AND id=-2", "WHERE id=-1 AND id=-2 AND id=-3" ...
|
||||
// and it conflicts with the "Iterate"'s internal states.
|
||||
// has, err := db.GetEngine(ctx).Get(&TestModel2{ID: -m1.ID})
|
||||
|
||||
actualCount++
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
assert.EqualValues(t, testCount, actualCount)
|
||||
|
||||
// deny the bad usages
|
||||
assert.PanicsWithError(t, "using database context in an iterator would cause corrupted results", func() {
|
||||
_ = unittest.GetXORMEngine().Iterate(&TestModel1{}, func(i int, bean any) error {
|
||||
_ = db.GetEngine(db.DefaultContext)
|
||||
return nil
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -16,30 +16,30 @@ import (
|
|||
|
||||
// ConvertDatabaseTable converts database and tables from utf8 to utf8mb4 if it's mysql and set ROW_FORMAT=dynamic
|
||||
func ConvertDatabaseTable() error {
|
||||
if x.Dialect().URI().DBType != schemas.MYSQL {
|
||||
if xormEngine.Dialect().URI().DBType != schemas.MYSQL {
|
||||
return nil
|
||||
}
|
||||
|
||||
r, err := CheckCollations(x)
|
||||
r, err := CheckCollations(xormEngine)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = x.Exec(fmt.Sprintf("ALTER DATABASE `%s` CHARACTER SET utf8mb4 COLLATE %s", setting.Database.Name, r.ExpectedCollation))
|
||||
_, err = xormEngine.Exec(fmt.Sprintf("ALTER DATABASE `%s` CHARACTER SET utf8mb4 COLLATE %s", setting.Database.Name, r.ExpectedCollation))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tables, err := x.DBMetas()
|
||||
tables, err := xormEngine.DBMetas()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, table := range tables {
|
||||
if _, err := x.Exec(fmt.Sprintf("ALTER TABLE `%s` ROW_FORMAT=dynamic", table.Name)); err != nil {
|
||||
if _, err := xormEngine.Exec(fmt.Sprintf("ALTER TABLE `%s` ROW_FORMAT=dynamic", table.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := x.Exec(fmt.Sprintf("ALTER TABLE `%s` CONVERT TO CHARACTER SET utf8mb4 COLLATE %s", table.Name, r.ExpectedCollation)); err != nil {
|
||||
if _, err := xormEngine.Exec(fmt.Sprintf("ALTER TABLE `%s` CONVERT TO CHARACTER SET utf8mb4 COLLATE %s", table.Name, r.ExpectedCollation)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -49,11 +49,11 @@ func ConvertDatabaseTable() error {
|
|||
|
||||
// ConvertVarcharToNVarchar converts database and tables from varchar to nvarchar if it's mssql
|
||||
func ConvertVarcharToNVarchar() error {
|
||||
if x.Dialect().URI().DBType != schemas.MSSQL {
|
||||
if xormEngine.Dialect().URI().DBType != schemas.MSSQL {
|
||||
return nil
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
sess := xormEngine.NewSession()
|
||||
defer sess.Close()
|
||||
res, err := sess.QuerySliceString(`SELECT 'ALTER TABLE ' + OBJECT_NAME(SC.object_id) + ' MODIFY SC.name NVARCHAR(' + CONVERT(VARCHAR(5),SC.max_length) + ')'
|
||||
FROM SYS.columns SC
|
||||
|
|
|
@ -8,17 +8,10 @@ import (
|
|||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/contexts"
|
||||
"xorm.io/xorm/names"
|
||||
"xorm.io/xorm/schemas"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql" // Needed for the MySQL driver
|
||||
|
@ -27,9 +20,9 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
x *xorm.Engine
|
||||
tables []any
|
||||
initFuncs []func() error
|
||||
xormEngine *xorm.Engine
|
||||
registeredModels []any
|
||||
registeredInitFuncs []func() error
|
||||
)
|
||||
|
||||
// Engine represents a xorm engine or session.
|
||||
|
@ -70,167 +63,38 @@ type Engine interface {
|
|||
|
||||
// TableInfo returns table's information via an object
|
||||
func TableInfo(v any) (*schemas.Table, error) {
|
||||
return x.TableInfo(v)
|
||||
return xormEngine.TableInfo(v)
|
||||
}
|
||||
|
||||
// DumpTables dump tables information
|
||||
func DumpTables(tables []*schemas.Table, w io.Writer, tp ...schemas.DBType) error {
|
||||
return x.DumpTables(tables, w, tp...)
|
||||
}
|
||||
|
||||
// RegisterModel registers model, if initfunc provided, it will be invoked after data model sync
|
||||
// RegisterModel registers model, if initFuncs provided, it will be invoked after data model sync
|
||||
func RegisterModel(bean any, initFunc ...func() error) {
|
||||
tables = append(tables, bean)
|
||||
if len(initFuncs) > 0 && initFunc[0] != nil {
|
||||
initFuncs = append(initFuncs, initFunc[0])
|
||||
registeredModels = append(registeredModels, bean)
|
||||
if len(registeredInitFuncs) > 0 && initFunc[0] != nil {
|
||||
registeredInitFuncs = append(registeredInitFuncs, initFunc[0])
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
gonicNames := []string{"SSL", "UID"}
|
||||
for _, name := range gonicNames {
|
||||
names.LintGonicMapper[name] = true
|
||||
}
|
||||
}
|
||||
|
||||
// newXORMEngine returns a new XORM engine from the configuration
|
||||
func newXORMEngine() (*xorm.Engine, error) {
|
||||
connStr, err := setting.DBConnStr()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var engine *xorm.Engine
|
||||
|
||||
if setting.Database.Type.IsPostgreSQL() && len(setting.Database.Schema) > 0 {
|
||||
// OK whilst we sort out our schema issues - create a schema aware postgres
|
||||
registerPostgresSchemaDriver()
|
||||
engine, err = xorm.NewEngine("postgresschema", connStr)
|
||||
} else {
|
||||
engine, err = xorm.NewEngine(setting.Database.Type.String(), connStr)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if setting.Database.Type == "mysql" {
|
||||
engine.Dialect().SetParams(map[string]string{"rowFormat": "DYNAMIC"})
|
||||
} else if setting.Database.Type == "mssql" {
|
||||
engine.Dialect().SetParams(map[string]string{"DEFAULT_VARCHAR": "nvarchar"})
|
||||
}
|
||||
engine.SetSchema(setting.Database.Schema)
|
||||
return engine, nil
|
||||
}
|
||||
|
||||
// SyncAllTables sync the schemas of all tables, is required by unit test code
|
||||
func SyncAllTables() error {
|
||||
_, err := x.StoreEngine("InnoDB").SyncWithOptions(xorm.SyncOptions{
|
||||
_, err := xormEngine.StoreEngine("InnoDB").SyncWithOptions(xorm.SyncOptions{
|
||||
WarnIfDatabaseColumnMissed: true,
|
||||
}, tables...)
|
||||
}, registeredModels...)
|
||||
return err
|
||||
}
|
||||
|
||||
// InitEngine initializes the xorm.Engine and sets it as db.DefaultContext
|
||||
func InitEngine(ctx context.Context) error {
|
||||
xormEngine, err := newXORMEngine()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to database: %w", err)
|
||||
}
|
||||
|
||||
xormEngine.SetMapper(names.GonicMapper{})
|
||||
// WARNING: for serv command, MUST remove the output to os.stdout,
|
||||
// so use log file to instead print to stdout.
|
||||
xormEngine.SetLogger(NewXORMLogger(setting.Database.LogSQL))
|
||||
xormEngine.ShowSQL(setting.Database.LogSQL)
|
||||
xormEngine.SetMaxOpenConns(setting.Database.MaxOpenConns)
|
||||
xormEngine.SetMaxIdleConns(setting.Database.MaxIdleConns)
|
||||
xormEngine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime)
|
||||
xormEngine.SetDefaultContext(ctx)
|
||||
|
||||
if setting.Database.SlowQueryThreshold > 0 {
|
||||
xormEngine.AddHook(&SlowQueryHook{
|
||||
Threshold: setting.Database.SlowQueryThreshold,
|
||||
Logger: log.GetLogger("xorm"),
|
||||
})
|
||||
}
|
||||
|
||||
SetDefaultEngine(ctx, xormEngine)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDefaultEngine sets the default engine for db
|
||||
func SetDefaultEngine(ctx context.Context, eng *xorm.Engine) {
|
||||
x = eng
|
||||
DefaultContext = &Context{
|
||||
Context: ctx,
|
||||
e: x,
|
||||
}
|
||||
}
|
||||
|
||||
// UnsetDefaultEngine closes and unsets the default engine
|
||||
// We hope the SetDefaultEngine and UnsetDefaultEngine can be paired, but it's impossible now,
|
||||
// there are many calls to InitEngine -> SetDefaultEngine directly to overwrite the `x` and DefaultContext without close
|
||||
// Global database engine related functions are all racy and there is no graceful close right now.
|
||||
func UnsetDefaultEngine() {
|
||||
if x != nil {
|
||||
_ = x.Close()
|
||||
x = nil
|
||||
}
|
||||
DefaultContext = nil
|
||||
}
|
||||
|
||||
// InitEngineWithMigration initializes a new xorm.Engine and sets it as the db.DefaultContext
|
||||
// This function must never call .Sync() if the provided migration function fails.
|
||||
// When called from the "doctor" command, the migration function is a version check
|
||||
// that prevents the doctor from fixing anything in the database if the migration level
|
||||
// is different from the expected value.
|
||||
func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err error) {
|
||||
if err = InitEngine(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = x.Ping(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preprocessDatabaseCollation(x)
|
||||
|
||||
// We have to run migrateFunc here in case the user is re-running installation on a previously created DB.
|
||||
// If we do not then table schemas will be changed and there will be conflicts when the migrations run properly.
|
||||
//
|
||||
// Installation should only be being re-run if users want to recover an old database.
|
||||
// However, we should think carefully about should we support re-install on an installed instance,
|
||||
// as there may be other problems due to secret reinitialization.
|
||||
if err = migrateFunc(x); err != nil {
|
||||
return fmt.Errorf("migrate: %w", err)
|
||||
}
|
||||
|
||||
if err = SyncAllTables(); err != nil {
|
||||
return fmt.Errorf("sync database struct error: %w", err)
|
||||
}
|
||||
|
||||
for _, initFunc := range initFuncs {
|
||||
if err := initFunc(); err != nil {
|
||||
return fmt.Errorf("initFunc failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NamesToBean return a list of beans or an error
|
||||
func NamesToBean(names ...string) ([]any, error) {
|
||||
beans := []any{}
|
||||
if len(names) == 0 {
|
||||
beans = append(beans, tables...)
|
||||
beans = append(beans, registeredModels...)
|
||||
return beans, nil
|
||||
}
|
||||
// Need to map provided names to beans...
|
||||
beanMap := make(map[string]any)
|
||||
for _, bean := range tables {
|
||||
for _, bean := range registeredModels {
|
||||
beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean
|
||||
beanMap[strings.ToLower(x.TableName(bean))] = bean
|
||||
beanMap[strings.ToLower(x.TableName(bean, true))] = bean
|
||||
beanMap[strings.ToLower(xormEngine.TableName(bean))] = bean
|
||||
beanMap[strings.ToLower(xormEngine.TableName(bean, true))] = bean
|
||||
}
|
||||
|
||||
gotBean := make(map[any]bool)
|
||||
|
@ -247,36 +111,9 @@ func NamesToBean(names ...string) ([]any, error) {
|
|||
return beans, nil
|
||||
}
|
||||
|
||||
// DumpDatabase dumps all data from database according the special database SQL syntax to file system.
|
||||
func DumpDatabase(filePath, dbType string) error {
|
||||
var tbs []*schemas.Table
|
||||
for _, t := range tables {
|
||||
t, err := x.TableInfo(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tbs = append(tbs, t)
|
||||
}
|
||||
|
||||
type Version struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Version int64
|
||||
}
|
||||
t, err := x.TableInfo(&Version{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tbs = append(tbs, t)
|
||||
|
||||
if len(dbType) > 0 {
|
||||
return x.DumpTablesToFile(tbs, filePath, schemas.DBType(dbType))
|
||||
}
|
||||
return x.DumpTablesToFile(tbs, filePath)
|
||||
}
|
||||
|
||||
// MaxBatchInsertSize returns the table's max batch insert size
|
||||
func MaxBatchInsertSize(bean any) int {
|
||||
t, err := x.TableInfo(bean)
|
||||
t, err := xormEngine.TableInfo(bean)
|
||||
if err != nil {
|
||||
return 50
|
||||
}
|
||||
|
@ -285,18 +122,18 @@ func MaxBatchInsertSize(bean any) int {
|
|||
|
||||
// IsTableNotEmpty returns true if table has at least one record
|
||||
func IsTableNotEmpty(beanOrTableName any) (bool, error) {
|
||||
return x.Table(beanOrTableName).Exist()
|
||||
return xormEngine.Table(beanOrTableName).Exist()
|
||||
}
|
||||
|
||||
// DeleteAllRecords will delete all the records of this table
|
||||
func DeleteAllRecords(tableName string) error {
|
||||
_, err := x.Exec(fmt.Sprintf("DELETE FROM %s", tableName))
|
||||
_, err := xormEngine.Exec(fmt.Sprintf("DELETE FROM %s", tableName))
|
||||
return err
|
||||
}
|
||||
|
||||
// GetMaxID will return max id of the table
|
||||
func GetMaxID(beanOrTableName any) (maxID int64, err error) {
|
||||
_, err = x.Select("MAX(id)").Table(beanOrTableName).Get(&maxID)
|
||||
_, err = xormEngine.Select("MAX(id)").Table(beanOrTableName).Get(&maxID)
|
||||
return maxID, err
|
||||
}
|
||||
|
||||
|
@ -308,24 +145,3 @@ func SetLogSQL(ctx context.Context, on bool) {
|
|||
sess.Engine().ShowSQL(on)
|
||||
}
|
||||
}
|
||||
|
||||
type SlowQueryHook struct {
|
||||
Threshold time.Duration
|
||||
Logger log.Logger
|
||||
}
|
||||
|
||||
var _ contexts.Hook = &SlowQueryHook{}
|
||||
|
||||
func (SlowQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
|
||||
return c.Ctx, nil
|
||||
}
|
||||
|
||||
func (h *SlowQueryHook) AfterProcess(c *contexts.ContextHook) error {
|
||||
if c.ExecuteTime >= h.Threshold {
|
||||
// 8 is the amount of skips passed to runtime.Caller, so that in the log the correct function
|
||||
// is being displayed (the function that ultimately wants to execute the query in the code)
|
||||
// instead of the function of the slow query hook being called.
|
||||
h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package db
|
||||
|
||||
import "xorm.io/xorm/schemas"
|
||||
|
||||
// DumpDatabase dumps all data from database according the special database SQL syntax to file system.
|
||||
func DumpDatabase(filePath, dbType string) error {
|
||||
var tbs []*schemas.Table
|
||||
for _, t := range registeredModels {
|
||||
t, err := xormEngine.TableInfo(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tbs = append(tbs, t)
|
||||
}
|
||||
|
||||
type Version struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Version int64
|
||||
}
|
||||
t, err := xormEngine.TableInfo(&Version{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tbs = append(tbs, t)
|
||||
|
||||
if dbType != "" {
|
||||
return xormEngine.DumpTablesToFile(tbs, filePath, schemas.DBType(dbType))
|
||||
}
|
||||
return xormEngine.DumpTablesToFile(tbs, filePath)
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/gtprof"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"xorm.io/xorm/contexts"
|
||||
)
|
||||
|
||||
type EngineHook struct {
|
||||
Threshold time.Duration
|
||||
Logger log.Logger
|
||||
}
|
||||
|
||||
var _ contexts.Hook = (*EngineHook)(nil)
|
||||
|
||||
func (*EngineHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
|
||||
ctx, _ := gtprof.GetTracer().Start(c.Ctx, gtprof.TraceSpanDatabase)
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func (h *EngineHook) AfterProcess(c *contexts.ContextHook) error {
|
||||
span := gtprof.GetContextSpan(c.Ctx)
|
||||
if span != nil {
|
||||
// Do not record SQL parameters here:
|
||||
// * It shouldn't expose the parameters because they contain sensitive information, end users need to report the trace details safely.
|
||||
// * Some parameters contain quite long texts, waste memory and are difficult to display.
|
||||
span.SetAttributeString(gtprof.TraceAttrDbSQL, c.SQL)
|
||||
span.End()
|
||||
} else {
|
||||
setting.PanicInDevOrTesting("span in database engine hook is nil")
|
||||
}
|
||||
if c.ExecuteTime >= h.Threshold {
|
||||
// 8 is the amount of skips passed to runtime.Caller, so that in the log the correct function
|
||||
// is being displayed (the function that ultimately wants to execute the query in the code)
|
||||
// instead of the function of the slow query hook being called.
|
||||
h.Logger.Log(8, &log.Event{Level: log.WARN}, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/names"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gonicNames := []string{"SSL", "UID"}
|
||||
for _, name := range gonicNames {
|
||||
names.LintGonicMapper[name] = true
|
||||
}
|
||||
}
|
||||
|
||||
// newXORMEngine returns a new XORM engine from the configuration
|
||||
func newXORMEngine() (*xorm.Engine, error) {
|
||||
connStr, err := setting.DBConnStr()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var engine *xorm.Engine
|
||||
|
||||
if setting.Database.Type.IsPostgreSQL() && len(setting.Database.Schema) > 0 {
|
||||
// OK whilst we sort out our schema issues - create a schema aware postgres
|
||||
registerPostgresSchemaDriver()
|
||||
engine, err = xorm.NewEngine("postgresschema", connStr)
|
||||
} else {
|
||||
engine, err = xorm.NewEngine(setting.Database.Type.String(), connStr)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if setting.Database.Type == "mysql" {
|
||||
engine.Dialect().SetParams(map[string]string{"rowFormat": "DYNAMIC"})
|
||||
} else if setting.Database.Type == "mssql" {
|
||||
engine.Dialect().SetParams(map[string]string{"DEFAULT_VARCHAR": "nvarchar"})
|
||||
}
|
||||
engine.SetSchema(setting.Database.Schema)
|
||||
return engine, nil
|
||||
}
|
||||
|
||||
// InitEngine initializes the xorm.Engine and sets it as db.DefaultContext
|
||||
func InitEngine(ctx context.Context) error {
|
||||
xe, err := newXORMEngine()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "SQLite3 support") {
|
||||
return fmt.Errorf(`sqlite3 requires: -tags sqlite,sqlite_unlock_notify%s%w`, "\n", err)
|
||||
}
|
||||
return fmt.Errorf("failed to connect to database: %w", err)
|
||||
}
|
||||
|
||||
xe.SetMapper(names.GonicMapper{})
|
||||
// WARNING: for serv command, MUST remove the output to os.stdout,
|
||||
// so use log file to instead print to stdout.
|
||||
xe.SetLogger(NewXORMLogger(setting.Database.LogSQL))
|
||||
xe.ShowSQL(setting.Database.LogSQL)
|
||||
xe.SetMaxOpenConns(setting.Database.MaxOpenConns)
|
||||
xe.SetMaxIdleConns(setting.Database.MaxIdleConns)
|
||||
xe.SetConnMaxLifetime(setting.Database.ConnMaxLifetime)
|
||||
xe.SetDefaultContext(ctx)
|
||||
|
||||
if setting.Database.SlowQueryThreshold > 0 {
|
||||
xe.AddHook(&EngineHook{
|
||||
Threshold: setting.Database.SlowQueryThreshold,
|
||||
Logger: log.GetLogger("xorm"),
|
||||
})
|
||||
}
|
||||
|
||||
SetDefaultEngine(ctx, xe)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetDefaultEngine sets the default engine for db
|
||||
func SetDefaultEngine(ctx context.Context, eng *xorm.Engine) {
|
||||
xormEngine = eng
|
||||
DefaultContext = &Context{Context: ctx, engine: xormEngine}
|
||||
}
|
||||
|
||||
// UnsetDefaultEngine closes and unsets the default engine
|
||||
// We hope the SetDefaultEngine and UnsetDefaultEngine can be paired, but it's impossible now,
|
||||
// there are many calls to InitEngine -> SetDefaultEngine directly to overwrite the `xormEngine` and DefaultContext without close
|
||||
// Global database engine related functions are all racy and there is no graceful close right now.
|
||||
func UnsetDefaultEngine() {
|
||||
if xormEngine != nil {
|
||||
_ = xormEngine.Close()
|
||||
xormEngine = nil
|
||||
}
|
||||
DefaultContext = nil
|
||||
}
|
||||
|
||||
// InitEngineWithMigration initializes a new xorm.Engine and sets it as the db.DefaultContext
|
||||
// This function must never call .Sync() if the provided migration function fails.
|
||||
// When called from the "doctor" command, the migration function is a version check
|
||||
// that prevents the doctor from fixing anything in the database if the migration level
|
||||
// is different from the expected value.
|
||||
func InitEngineWithMigration(ctx context.Context, migrateFunc func(context.Context, *xorm.Engine) error) (err error) {
|
||||
if err = InitEngine(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = xormEngine.Ping(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preprocessDatabaseCollation(xormEngine)
|
||||
|
||||
// We have to run migrateFunc here in case the user is re-running installation on a previously created DB.
|
||||
// If we do not then table schemas will be changed and there will be conflicts when the migrations run properly.
|
||||
//
|
||||
// Installation should only be being re-run if users want to recover an old database.
|
||||
// However, we should think carefully about should we support re-install on an installed instance,
|
||||
// as there may be other problems due to secret reinitialization.
|
||||
if err = migrateFunc(ctx, xormEngine); err != nil {
|
||||
return fmt.Errorf("migrate: %w", err)
|
||||
}
|
||||
|
||||
if err = SyncAllTables(); err != nil {
|
||||
return fmt.Errorf("sync database struct error: %w", err)
|
||||
}
|
||||
|
||||
for _, initFunc := range registeredInitFuncs {
|
||||
if err := initFunc(); err != nil {
|
||||
return fmt.Errorf("initFunc failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -15,6 +15,7 @@ import (
|
|||
_ "code.gitea.io/gitea/cmd" // for TestPrimaryKeys
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDumpDatabase(t *testing.T) {
|
||||
|
@ -62,9 +63,7 @@ func TestPrimaryKeys(t *testing.T) {
|
|||
// Import "code.gitea.io/gitea/cmd" to make sure each db.RegisterModel in init functions has been called.
|
||||
|
||||
beans, err := db.NamesToBean()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
whitelist := map[string]string{
|
||||
"the_table_name_to_skip_checking": "Write a note here to explain why",
|
||||
|
@ -79,8 +78,6 @@ func TestPrimaryKeys(t *testing.T) {
|
|||
t.Logf("ignore %q because %q", table.Name, why)
|
||||
continue
|
||||
}
|
||||
if len(table.PrimaryKeys) == 0 {
|
||||
t.Errorf("table %q has no primary key", table.Name)
|
||||
}
|
||||
assert.NotEmpty(t, table.PrimaryKeys, "table %q has no primary key", table.Name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
)
|
||||
|
||||
func getXORMEngine() *xorm.Engine {
|
||||
return db.DefaultContext.(*db.Context).Engine().(*xorm.Engine)
|
||||
return db.GetEngine(db.DefaultContext).(*xorm.Engine)
|
||||
}
|
||||
|
||||
// CheckDatabaseConnection checks the database connection
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// Iterate iterate all the Bean object
|
||||
// Iterate iterates all the Bean object
|
||||
func Iterate[Bean any](ctx context.Context, cond builder.Cond, f func(ctx context.Context, bean *Bean) error) error {
|
||||
var start int
|
||||
batchSize := setting.Database.IterateBufferSize
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue