mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-12 16:35:12 +02:00
Merge branch 'main' into add-file-tree-to-file-view-page
This commit is contained in:
commit
07fb84f830
999
.eslintrc.cjs
Normal file
999
.eslintrc.cjs
Normal file
@ -0,0 +1,999 @@
|
||||
const restrictedSyntax = ['WithStatement', 'ForInStatement', 'LabeledStatement', 'SequenceExpression'];
|
||||
|
||||
module.exports = {
|
||||
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-x/extensions': ['.js', '.ts'],
|
||||
'import-x/parsers': {
|
||||
'@typescript-eslint/parser': ['.js', '.ts'],
|
||||
},
|
||||
'import-x/resolver': {
|
||||
typescript: true,
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
'@eslint-community/eslint-plugin-eslint-comments',
|
||||
'@stylistic/eslint-plugin-js',
|
||||
'@typescript-eslint/eslint-plugin',
|
||||
'eslint-plugin-array-func',
|
||||
'eslint-plugin-github',
|
||||
'eslint-plugin-import-x',
|
||||
'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: ['*.config.*'],
|
||||
rules: {
|
||||
'import-x/no-unused-modules': [0],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.d.ts'],
|
||||
rules: {
|
||||
'import-x/no-unused-modules': [0],
|
||||
'@typescript-eslint/consistent-type-definitions': [0],
|
||||
'@typescript-eslint/consistent-type-imports': [0],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['web_src/js/types.ts'],
|
||||
rules: {
|
||||
'import-x/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, ...restrictedSyntax],
|
||||
},
|
||||
},
|
||||
{
|
||||
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-deprecated': [2],
|
||||
'@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],
|
||||
'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],
|
||||
'import-x/consistent-type-specifier-style': [0],
|
||||
'import-x/default': [0],
|
||||
'import-x/dynamic-import-chunkname': [0],
|
||||
'import-x/export': [2],
|
||||
'import-x/exports-last': [0],
|
||||
'import-x/extensions': [2, 'always', {ignorePackages: true}],
|
||||
'import-x/first': [2],
|
||||
'import-x/group-exports': [0],
|
||||
'import-x/max-dependencies': [0],
|
||||
'import-x/named': [2],
|
||||
'import-x/namespace': [0],
|
||||
'import-x/newline-after-import': [0],
|
||||
'import-x/no-absolute-path': [0],
|
||||
'import-x/no-amd': [2],
|
||||
'import-x/no-anonymous-default-export': [0],
|
||||
'import-x/no-commonjs': [2],
|
||||
'import-x/no-cycle': [2, {ignoreExternal: true, maxDepth: 1}],
|
||||
'import-x/no-default-export': [0],
|
||||
'import-x/no-deprecated': [0],
|
||||
'import-x/no-dynamic-require': [0],
|
||||
'import-x/no-empty-named-blocks': [2],
|
||||
'import-x/no-extraneous-dependencies': [2],
|
||||
'import-x/no-import-module-exports': [0],
|
||||
'import-x/no-internal-modules': [0],
|
||||
'import-x/no-mutable-exports': [0],
|
||||
'import-x/no-named-as-default-member': [0],
|
||||
'import-x/no-named-as-default': [0],
|
||||
'import-x/no-named-default': [0],
|
||||
'import-x/no-named-export': [0],
|
||||
'import-x/no-namespace': [0],
|
||||
'import-x/no-nodejs-modules': [0],
|
||||
'import-x/no-relative-packages': [0],
|
||||
'import-x/no-relative-parent-imports': [0],
|
||||
'import-x/no-restricted-paths': [0],
|
||||
'import-x/no-self-import': [2],
|
||||
'import-x/no-unassigned-import': [0],
|
||||
'import-x/no-unresolved': [2, {commonjs: true, ignore: ['\\?.+$']}],
|
||||
'import-x/no-unused-modules': [2, {unusedExports: true}],
|
||||
'import-x/no-useless-path-segments': [2, {commonjs: true}],
|
||||
'import-x/no-webpack-loader-syntax': [2],
|
||||
'import-x/order': [0],
|
||||
'import-x/prefer-default-export': [0],
|
||||
'import-x/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-done-fail': [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-fx': [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', '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'],
|
||||
'no-restricted-imports': [0],
|
||||
'no-restricted-syntax': [2, ...restrictedSyntax, {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': [0],
|
||||
'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-existence-index-check': [0],
|
||||
'unicorn/consistent-function-scoping': [0],
|
||||
'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-global-this': [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-min-max': [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'],
|
||||
},
|
||||
};
|
967
.eslintrc.yaml
967
.eslintrc.yaml
@ -1,967 +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-x/extensions: [".js", ".ts"]
|
||||
import-x/parsers:
|
||||
"@typescript-eslint/parser": [".js", ".ts"]
|
||||
import-x/resolver:
|
||||
typescript: true
|
||||
|
||||
plugins:
|
||||
- "@eslint-community/eslint-plugin-eslint-comments"
|
||||
- "@stylistic/eslint-plugin-js"
|
||||
- "@typescript-eslint/eslint-plugin"
|
||||
- eslint-plugin-array-func
|
||||
- eslint-plugin-github
|
||||
- eslint-plugin-import-x
|
||||
- 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:
|
||||
import-x/no-unused-modules: [0]
|
||||
- files: ["**/*.d.ts"]
|
||||
rules:
|
||||
import-x/no-unused-modules: [0]
|
||||
"@typescript-eslint/consistent-type-definitions": [0]
|
||||
"@typescript-eslint/consistent-type-imports": [0]
|
||||
- files: ["web_src/js/types.ts"]
|
||||
rules:
|
||||
import-x/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-deprecated": [2]
|
||||
"@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]
|
||||
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]
|
||||
import-x/consistent-type-specifier-style: [0]
|
||||
import-x/default: [0]
|
||||
import-x/dynamic-import-chunkname: [0]
|
||||
import-x/export: [2]
|
||||
import-x/exports-last: [0]
|
||||
import-x/extensions: [2, always, {ignorePackages: true}]
|
||||
import-x/first: [2]
|
||||
import-x/group-exports: [0]
|
||||
import-x/max-dependencies: [0]
|
||||
import-x/named: [2]
|
||||
import-x/namespace: [0]
|
||||
import-x/newline-after-import: [0]
|
||||
import-x/no-absolute-path: [0]
|
||||
import-x/no-amd: [2]
|
||||
import-x/no-anonymous-default-export: [0]
|
||||
import-x/no-commonjs: [2]
|
||||
import-x/no-cycle: [2, {ignoreExternal: true, maxDepth: 1}]
|
||||
import-x/no-default-export: [0]
|
||||
import-x/no-deprecated: [0]
|
||||
import-x/no-dynamic-require: [0]
|
||||
import-x/no-empty-named-blocks: [2]
|
||||
import-x/no-extraneous-dependencies: [2]
|
||||
import-x/no-import-module-exports: [0]
|
||||
import-x/no-internal-modules: [0]
|
||||
import-x/no-mutable-exports: [0]
|
||||
import-x/no-named-as-default-member: [0]
|
||||
import-x/no-named-as-default: [0]
|
||||
import-x/no-named-default: [0]
|
||||
import-x/no-named-export: [0]
|
||||
import-x/no-namespace: [0]
|
||||
import-x/no-nodejs-modules: [0]
|
||||
import-x/no-relative-packages: [0]
|
||||
import-x/no-relative-parent-imports: [0]
|
||||
import-x/no-restricted-paths: [0]
|
||||
import-x/no-self-import: [2]
|
||||
import-x/no-unassigned-import: [0]
|
||||
import-x/no-unresolved: [2, {commonjs: true, ignore: ["\\?.+$"]}]
|
||||
import-x/no-unused-modules: [2, {unusedExports: true}]
|
||||
import-x/no-useless-path-segments: [2, {commonjs: true}]
|
||||
import-x/no-webpack-loader-syntax: [2]
|
||||
import-x/order: [0]
|
||||
import-x/prefer-default-export: [0]
|
||||
import-x/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-done-fail: [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-fx: [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}] # TODO: disable this rule after tsc passes
|
||||
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-existence-index-check: [0]
|
||||
unicorn/consistent-function-scoping: [0]
|
||||
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-global-this: [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-min-max: [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]
|
@ -1,5 +1,5 @@
|
||||
# Build stage
|
||||
FROM docker.io/library/golang:1.23-alpine3.20 AS build-env
|
||||
FROM docker.io/library/golang:1.23-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.23-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
|
||||
|
@ -46,7 +46,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)
|
||||
|
8
Makefile
8
Makefile
@ -26,17 +26,17 @@ COMMA := ,
|
||||
XGO_VERSION := go-1.23.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.0.3
|
||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2
|
||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
|
||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.5.1
|
||||
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.0
|
||||
|
||||
DOCKER_IMAGE ?= gitea/gitea
|
||||
DOCKER_TAG ?= latest
|
||||
|
2
go.mod
2
go.mod
@ -123,7 +123,7 @@ require (
|
||||
github.com/yuin/goldmark-meta v1.1.0
|
||||
golang.org/x/crypto v0.31.0
|
||||
golang.org/x/image v0.21.0
|
||||
golang.org/x/net v0.30.0
|
||||
golang.org/x/net v0.33.0
|
||||
golang.org/x/oauth2 v0.23.0
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/sys v0.28.0
|
||||
|
4
go.sum
4
go.sum
@ -932,8 +932,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -153,28 +153,34 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
|
||||
}
|
||||
|
||||
func AggregateJobStatus(jobs []*ActionRunJob) Status {
|
||||
allDone := true
|
||||
allWaiting := true
|
||||
hasFailure := false
|
||||
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
|
||||
}
|
||||
|
85
models/actions/run_job_status_test.go
Normal file
85
models/actions/run_job_status_test.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
@ -511,7 +511,7 @@ func ActivityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.
|
||||
}
|
||||
|
||||
if opts.RequestedTeam != nil {
|
||||
env := organization.OrgFromUser(opts.RequestedUser).AccessibleTeamReposEnv(ctx, opts.RequestedTeam)
|
||||
env := repo_model.AccessibleTeamReposEnv(ctx, organization.OrgFromUser(opts.RequestedUser), opts.RequestedTeam)
|
||||
teamRepoIDs, err := env.RepoIDs(1, opts.RequestedUser.NumRepos)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetTeamRepositories: %w", err)
|
||||
|
@ -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,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"`)
|
||||
}
|
||||
|
@ -140,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 {
|
||||
|
@ -94,7 +94,7 @@ func GetEngine(ctx context.Context) Engine {
|
||||
if e := getExistingEngine(ctx); e != nil {
|
||||
return e
|
||||
}
|
||||
return x.Context(ctx)
|
||||
return xormEngine.Context(ctx)
|
||||
}
|
||||
|
||||
// getExistingEngine gets an existing db Engine/Statement from this context or returns nil
|
||||
@ -155,7 +155,7 @@ func TxContext(parentCtx context.Context) (*Context, Committer, error) {
|
||||
return newContext(parentCtx, sess), &halfCommitter{committer: sess}, nil
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
sess := xormEngine.NewSession()
|
||||
if err := sess.Begin(); err != nil {
|
||||
_ = sess.Close()
|
||||
return nil, nil, err
|
||||
@ -179,7 +179,7 @@ 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
|
||||
@ -322,7 +322,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
|
||||
|
@ -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 {
|
||||
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)
|
||||
}
|
||||
|
||||
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, engine: 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
|
||||
}
|
||||
|
33
models/db/engine_dump.go
Normal file
33
models/db/engine_dump.go
Normal file
@ -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)
|
||||
}
|
34
models/db/engine_hook.go
Normal file
34
models/db/engine_hook.go
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"xorm.io/xorm/contexts"
|
||||
)
|
||||
|
||||
type SlowQueryHook struct {
|
||||
Threshold time.Duration
|
||||
Logger log.Logger
|
||||
}
|
||||
|
||||
var _ contexts.Hook = (*SlowQueryHook)(nil)
|
||||
|
||||
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
|
||||
}
|
140
models/db/engine_init.go
Normal file
140
models/db/engine_init.go
Normal file
@ -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(&SlowQueryHook{
|
||||
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(*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(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
|
||||
}
|
@ -16,7 +16,7 @@ var (
|
||||
// ErrNameEmpty name is empty error
|
||||
ErrNameEmpty = util.SilentWrap{Message: "name is empty", Err: util.ErrInvalidArgument}
|
||||
|
||||
// AlphaDashDotPattern characters prohibited in a user name (anything except A-Za-z0-9_.-)
|
||||
// AlphaDashDotPattern characters prohibited in a username (anything except A-Za-z0-9_.-)
|
||||
AlphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||
)
|
||||
|
||||
|
@ -17,11 +17,11 @@ func CountBadSequences(_ context.Context) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
sess := xormEngine.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
var sequences []string
|
||||
schema := x.Dialect().URI().Schema
|
||||
schema := xormEngine.Dialect().URI().Schema
|
||||
|
||||
sess.Engine().SetSchema("")
|
||||
if err := sess.Table("information_schema.sequences").Cols("sequence_name").Where("sequence_name LIKE 'tmp_recreate__%_id_seq%' AND sequence_catalog = ?", setting.Database.Name).Find(&sequences); err != nil {
|
||||
@ -38,7 +38,7 @@ func FixBadSequences(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
sess := x.NewSession()
|
||||
sess := xormEngine.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
|
552
models/error.go
552
models/error.go
@ -1,552 +0,0 @@
|
||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// ErrUserOwnRepos represents a "UserOwnRepos" kind of error.
|
||||
type ErrUserOwnRepos struct {
|
||||
UID int64
|
||||
}
|
||||
|
||||
// IsErrUserOwnRepos checks if an error is a ErrUserOwnRepos.
|
||||
func IsErrUserOwnRepos(err error) bool {
|
||||
_, ok := err.(ErrUserOwnRepos)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUserOwnRepos) Error() string {
|
||||
return fmt.Sprintf("user still has ownership of repositories [uid: %d]", err.UID)
|
||||
}
|
||||
|
||||
// ErrUserHasOrgs represents a "UserHasOrgs" kind of error.
|
||||
type ErrUserHasOrgs struct {
|
||||
UID int64
|
||||
}
|
||||
|
||||
// IsErrUserHasOrgs checks if an error is a ErrUserHasOrgs.
|
||||
func IsErrUserHasOrgs(err error) bool {
|
||||
_, ok := err.(ErrUserHasOrgs)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUserHasOrgs) Error() string {
|
||||
return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID)
|
||||
}
|
||||
|
||||
// ErrUserOwnPackages notifies that the user (still) owns the packages.
|
||||
type ErrUserOwnPackages struct {
|
||||
UID int64
|
||||
}
|
||||
|
||||
// IsErrUserOwnPackages checks if an error is an ErrUserOwnPackages.
|
||||
func IsErrUserOwnPackages(err error) bool {
|
||||
_, ok := err.(ErrUserOwnPackages)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUserOwnPackages) Error() string {
|
||||
return fmt.Sprintf("user still has ownership of packages [uid: %d]", err.UID)
|
||||
}
|
||||
|
||||
// ErrDeleteLastAdminUser represents a "DeleteLastAdminUser" kind of error.
|
||||
type ErrDeleteLastAdminUser struct {
|
||||
UID int64
|
||||
}
|
||||
|
||||
// IsErrDeleteLastAdminUser checks if an error is a ErrDeleteLastAdminUser.
|
||||
func IsErrDeleteLastAdminUser(err error) bool {
|
||||
_, ok := err.(ErrDeleteLastAdminUser)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrDeleteLastAdminUser) Error() string {
|
||||
return fmt.Sprintf("can not delete the last admin user [uid: %d]", err.UID)
|
||||
}
|
||||
|
||||
// ErrNoPendingRepoTransfer is an error type for repositories without a pending
|
||||
// transfer request
|
||||
type ErrNoPendingRepoTransfer struct {
|
||||
RepoID int64
|
||||
}
|
||||
|
||||
func (err ErrNoPendingRepoTransfer) Error() string {
|
||||
return fmt.Sprintf("repository doesn't have a pending transfer [repo_id: %d]", err.RepoID)
|
||||
}
|
||||
|
||||
// IsErrNoPendingTransfer is an error type when a repository has no pending
|
||||
// transfers
|
||||
func IsErrNoPendingTransfer(err error) bool {
|
||||
_, ok := err.(ErrNoPendingRepoTransfer)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrNoPendingRepoTransfer) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrRepoTransferInProgress represents the state of a repository that has an
|
||||
// ongoing transfer
|
||||
type ErrRepoTransferInProgress struct {
|
||||
Uname string
|
||||
Name string
|
||||
}
|
||||
|
||||
// IsErrRepoTransferInProgress checks if an error is a ErrRepoTransferInProgress.
|
||||
func IsErrRepoTransferInProgress(err error) bool {
|
||||
_, ok := err.(ErrRepoTransferInProgress)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrRepoTransferInProgress) Error() string {
|
||||
return fmt.Sprintf("repository is already being transferred [uname: %s, name: %s]", err.Uname, err.Name)
|
||||
}
|
||||
|
||||
func (err ErrRepoTransferInProgress) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
|
||||
type ErrInvalidCloneAddr struct {
|
||||
Host string
|
||||
IsURLError bool
|
||||
IsInvalidPath bool
|
||||
IsProtocolInvalid bool
|
||||
IsPermissionDenied bool
|
||||
LocalPath bool
|
||||
}
|
||||
|
||||
// IsErrInvalidCloneAddr checks if an error is a ErrInvalidCloneAddr.
|
||||
func IsErrInvalidCloneAddr(err error) bool {
|
||||
_, ok := err.(*ErrInvalidCloneAddr)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err *ErrInvalidCloneAddr) Error() string {
|
||||
if err.IsInvalidPath {
|
||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided path is invalid", err.Host)
|
||||
}
|
||||
if err.IsProtocolInvalid {
|
||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url protocol is not allowed", err.Host)
|
||||
}
|
||||
if err.IsPermissionDenied {
|
||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed.", err.Host)
|
||||
}
|
||||
if err.IsURLError {
|
||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url is invalid", err.Host)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed", err.Host)
|
||||
}
|
||||
|
||||
func (err *ErrInvalidCloneAddr) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// ErrUpdateTaskNotExist represents a "UpdateTaskNotExist" kind of error.
|
||||
type ErrUpdateTaskNotExist struct {
|
||||
UUID string
|
||||
}
|
||||
|
||||
// IsErrUpdateTaskNotExist checks if an error is a ErrUpdateTaskNotExist.
|
||||
func IsErrUpdateTaskNotExist(err error) bool {
|
||||
_, ok := err.(ErrUpdateTaskNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUpdateTaskNotExist) Error() string {
|
||||
return fmt.Sprintf("update task does not exist [uuid: %s]", err.UUID)
|
||||
}
|
||||
|
||||
func (err ErrUpdateTaskNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrInvalidTagName represents a "InvalidTagName" kind of error.
|
||||
type ErrInvalidTagName struct {
|
||||
TagName string
|
||||
}
|
||||
|
||||
// IsErrInvalidTagName checks if an error is a ErrInvalidTagName.
|
||||
func IsErrInvalidTagName(err error) bool {
|
||||
_, ok := err.(ErrInvalidTagName)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrInvalidTagName) Error() string {
|
||||
return fmt.Sprintf("release tag name is not valid [tag_name: %s]", err.TagName)
|
||||
}
|
||||
|
||||
func (err ErrInvalidTagName) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// ErrProtectedTagName represents a "ProtectedTagName" kind of error.
|
||||
type ErrProtectedTagName struct {
|
||||
TagName string
|
||||
}
|
||||
|
||||
// IsErrProtectedTagName checks if an error is a ErrProtectedTagName.
|
||||
func IsErrProtectedTagName(err error) bool {
|
||||
_, ok := err.(ErrProtectedTagName)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrProtectedTagName) Error() string {
|
||||
return fmt.Sprintf("release tag name is protected [tag_name: %s]", err.TagName)
|
||||
}
|
||||
|
||||
func (err ErrProtectedTagName) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// ErrRepoFileAlreadyExists represents a "RepoFileAlreadyExist" kind of error.
|
||||
type ErrRepoFileAlreadyExists struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// IsErrRepoFileAlreadyExists checks if an error is a ErrRepoFileAlreadyExists.
|
||||
func IsErrRepoFileAlreadyExists(err error) bool {
|
||||
_, ok := err.(ErrRepoFileAlreadyExists)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrRepoFileAlreadyExists) Error() string {
|
||||
return fmt.Sprintf("repository file already exists [path: %s]", err.Path)
|
||||
}
|
||||
|
||||
func (err ErrRepoFileAlreadyExists) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrRepoFileDoesNotExist represents a "RepoFileDoesNotExist" kind of error.
|
||||
type ErrRepoFileDoesNotExist struct {
|
||||
Path string
|
||||
Name string
|
||||
}
|
||||
|
||||
// IsErrRepoFileDoesNotExist checks if an error is a ErrRepoDoesNotExist.
|
||||
func IsErrRepoFileDoesNotExist(err error) bool {
|
||||
_, ok := err.(ErrRepoFileDoesNotExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrRepoFileDoesNotExist) Error() string {
|
||||
return fmt.Sprintf("repository file does not exist [path: %s]", err.Path)
|
||||
}
|
||||
|
||||
func (err ErrRepoFileDoesNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrFilenameInvalid represents a "FilenameInvalid" kind of error.
|
||||
type ErrFilenameInvalid struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
// IsErrFilenameInvalid checks if an error is an ErrFilenameInvalid.
|
||||
func IsErrFilenameInvalid(err error) bool {
|
||||
_, ok := err.(ErrFilenameInvalid)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrFilenameInvalid) Error() string {
|
||||
return fmt.Sprintf("path contains a malformed path component [path: %s]", err.Path)
|
||||
}
|
||||
|
||||
func (err ErrFilenameInvalid) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// ErrUserCannotCommit represents "UserCannotCommit" kind of error.
|
||||
type ErrUserCannotCommit struct {
|
||||
UserName string
|
||||
}
|
||||
|
||||
// IsErrUserCannotCommit checks if an error is an ErrUserCannotCommit.
|
||||
func IsErrUserCannotCommit(err error) bool {
|
||||
_, ok := err.(ErrUserCannotCommit)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUserCannotCommit) Error() string {
|
||||
return fmt.Sprintf("user cannot commit to repo [user: %s]", err.UserName)
|
||||
}
|
||||
|
||||
func (err ErrUserCannotCommit) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// ErrFilePathInvalid represents a "FilePathInvalid" kind of error.
|
||||
type ErrFilePathInvalid struct {
|
||||
Message string
|
||||
Path string
|
||||
Name string
|
||||
Type git.EntryMode
|
||||
}
|
||||
|
||||
// IsErrFilePathInvalid checks if an error is an ErrFilePathInvalid.
|
||||
func IsErrFilePathInvalid(err error) bool {
|
||||
_, ok := err.(ErrFilePathInvalid)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrFilePathInvalid) Error() string {
|
||||
if err.Message != "" {
|
||||
return err.Message
|
||||
}
|
||||
return fmt.Sprintf("path is invalid [path: %s]", err.Path)
|
||||
}
|
||||
|
||||
func (err ErrFilePathInvalid) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// ErrFilePathProtected represents a "FilePathProtected" kind of error.
|
||||
type ErrFilePathProtected struct {
|
||||
Message string
|
||||
Path string
|
||||
}
|
||||
|
||||
// IsErrFilePathProtected checks if an error is an ErrFilePathProtected.
|
||||
func IsErrFilePathProtected(err error) bool {
|
||||
_, ok := err.(ErrFilePathProtected)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrFilePathProtected) Error() string {
|
||||
if err.Message != "" {
|
||||
return err.Message
|
||||
}
|
||||
return fmt.Sprintf("path is protected and can not be changed [path: %s]", err.Path)
|
||||
}
|
||||
|
||||
func (err ErrFilePathProtected) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// ErrDisallowedToMerge represents an error that a branch is protected and the current user is not allowed to modify it.
|
||||
type ErrDisallowedToMerge struct {
|
||||
Reason string
|
||||
}
|
||||
|
||||
// IsErrDisallowedToMerge checks if an error is an ErrDisallowedToMerge.
|
||||
func IsErrDisallowedToMerge(err error) bool {
|
||||
_, ok := err.(ErrDisallowedToMerge)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrDisallowedToMerge) Error() string {
|
||||
return fmt.Sprintf("not allowed to merge [reason: %s]", err.Reason)
|
||||
}
|
||||
|
||||
func (err ErrDisallowedToMerge) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// ErrTagAlreadyExists represents an error that tag with such name already exists.
|
||||
type ErrTagAlreadyExists struct {
|
||||
TagName string
|
||||
}
|
||||
|
||||
// IsErrTagAlreadyExists checks if an error is an ErrTagAlreadyExists.
|
||||
func IsErrTagAlreadyExists(err error) bool {
|
||||
_, ok := err.(ErrTagAlreadyExists)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrTagAlreadyExists) Error() string {
|
||||
return fmt.Sprintf("tag already exists [name: %s]", err.TagName)
|
||||
}
|
||||
|
||||
func (err ErrTagAlreadyExists) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrSHADoesNotMatch represents a "SHADoesNotMatch" kind of error.
|
||||
type ErrSHADoesNotMatch struct {
|
||||
Path string
|
||||
GivenSHA string
|
||||
CurrentSHA string
|
||||
}
|
||||
|
||||
// IsErrSHADoesNotMatch checks if an error is a ErrSHADoesNotMatch.
|
||||
func IsErrSHADoesNotMatch(err error) bool {
|
||||
_, ok := err.(ErrSHADoesNotMatch)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrSHADoesNotMatch) Error() string {
|
||||
return fmt.Sprintf("sha does not match [given: %s, expected: %s]", err.GivenSHA, err.CurrentSHA)
|
||||
}
|
||||
|
||||
// ErrSHANotFound represents a "SHADoesNotMatch" kind of error.
|
||||
type ErrSHANotFound struct {
|
||||
SHA string
|
||||
}
|
||||
|
||||
// IsErrSHANotFound checks if an error is a ErrSHANotFound.
|
||||
func IsErrSHANotFound(err error) bool {
|
||||
_, ok := err.(ErrSHANotFound)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrSHANotFound) Error() string {
|
||||
return fmt.Sprintf("sha not found [%s]", err.SHA)
|
||||
}
|
||||
|
||||
func (err ErrSHANotFound) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrCommitIDDoesNotMatch represents a "CommitIDDoesNotMatch" kind of error.
|
||||
type ErrCommitIDDoesNotMatch struct {
|
||||
GivenCommitID string
|
||||
CurrentCommitID string
|
||||
}
|
||||
|
||||
// IsErrCommitIDDoesNotMatch checks if an error is a ErrCommitIDDoesNotMatch.
|
||||
func IsErrCommitIDDoesNotMatch(err error) bool {
|
||||
_, ok := err.(ErrCommitIDDoesNotMatch)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrCommitIDDoesNotMatch) Error() string {
|
||||
return fmt.Sprintf("file CommitID does not match [given: %s, expected: %s]", err.GivenCommitID, err.CurrentCommitID)
|
||||
}
|
||||
|
||||
// ErrSHAOrCommitIDNotProvided represents a "SHAOrCommitIDNotProvided" kind of error.
|
||||
type ErrSHAOrCommitIDNotProvided struct{}
|
||||
|
||||
// IsErrSHAOrCommitIDNotProvided checks if an error is a ErrSHAOrCommitIDNotProvided.
|
||||
func IsErrSHAOrCommitIDNotProvided(err error) bool {
|
||||
_, ok := err.(ErrSHAOrCommitIDNotProvided)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrSHAOrCommitIDNotProvided) Error() string {
|
||||
return "a SHA or commit ID must be proved when updating a file"
|
||||
}
|
||||
|
||||
// ErrInvalidMergeStyle represents an error if merging with disabled merge strategy
|
||||
type ErrInvalidMergeStyle struct {
|
||||
ID int64
|
||||
Style repo_model.MergeStyle
|
||||
}
|
||||
|
||||
// IsErrInvalidMergeStyle checks if an error is a ErrInvalidMergeStyle.
|
||||
func IsErrInvalidMergeStyle(err error) bool {
|
||||
_, ok := err.(ErrInvalidMergeStyle)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrInvalidMergeStyle) Error() string {
|
||||
return fmt.Sprintf("merge strategy is not allowed or is invalid [repo_id: %d, strategy: %s]",
|
||||
err.ID, err.Style)
|
||||
}
|
||||
|
||||
func (err ErrInvalidMergeStyle) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// ErrMergeConflicts represents an error if merging fails with a conflict
|
||||
type ErrMergeConflicts struct {
|
||||
Style repo_model.MergeStyle
|
||||
StdOut string
|
||||
StdErr string
|
||||
Err error
|
||||
}
|
||||
|
||||
// IsErrMergeConflicts checks if an error is a ErrMergeConflicts.
|
||||
func IsErrMergeConflicts(err error) bool {
|
||||
_, ok := err.(ErrMergeConflicts)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrMergeConflicts) Error() string {
|
||||
return fmt.Sprintf("Merge Conflict Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
||||
}
|
||||
|
||||
// ErrMergeUnrelatedHistories represents an error if merging fails due to unrelated histories
|
||||
type ErrMergeUnrelatedHistories struct {
|
||||
Style repo_model.MergeStyle
|
||||
StdOut string
|
||||
StdErr string
|
||||
Err error
|
||||
}
|
||||
|
||||
// IsErrMergeUnrelatedHistories checks if an error is a ErrMergeUnrelatedHistories.
|
||||
func IsErrMergeUnrelatedHistories(err error) bool {
|
||||
_, ok := err.(ErrMergeUnrelatedHistories)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrMergeUnrelatedHistories) Error() string {
|
||||
return fmt.Sprintf("Merge UnrelatedHistories Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
||||
}
|
||||
|
||||
// ErrMergeDivergingFastForwardOnly represents an error if a fast-forward-only merge fails because the branches diverge
|
||||
type ErrMergeDivergingFastForwardOnly struct {
|
||||
StdOut string
|
||||
StdErr string
|
||||
Err error
|
||||
}
|
||||
|
||||
// IsErrMergeDivergingFastForwardOnly checks if an error is a ErrMergeDivergingFastForwardOnly.
|
||||
func IsErrMergeDivergingFastForwardOnly(err error) bool {
|
||||
_, ok := err.(ErrMergeDivergingFastForwardOnly)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrMergeDivergingFastForwardOnly) Error() string {
|
||||
return fmt.Sprintf("Merge DivergingFastForwardOnly Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
|
||||
}
|
||||
|
||||
// ErrRebaseConflicts represents an error if rebase fails with a conflict
|
||||
type ErrRebaseConflicts struct {
|
||||
Style repo_model.MergeStyle
|
||||
CommitSHA string
|
||||
StdOut string
|
||||
StdErr string
|
||||
Err error
|
||||
}
|
||||
|
||||
// IsErrRebaseConflicts checks if an error is a ErrRebaseConflicts.
|
||||
func IsErrRebaseConflicts(err error) bool {
|
||||
_, ok := err.(ErrRebaseConflicts)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrRebaseConflicts) Error() string {
|
||||
return fmt.Sprintf("Rebase Error: %v: Whilst Rebasing: %s\n%s\n%s", err.Err, err.CommitSHA, err.StdErr, err.StdOut)
|
||||
}
|
||||
|
||||
// ErrPullRequestHasMerged represents a "PullRequestHasMerged"-error
|
||||
type ErrPullRequestHasMerged struct {
|
||||
ID int64
|
||||
IssueID int64
|
||||
HeadRepoID int64
|
||||
BaseRepoID int64
|
||||
HeadBranch string
|
||||
BaseBranch string
|
||||
}
|
||||
|
||||
// IsErrPullRequestHasMerged checks if an error is a ErrPullRequestHasMerged.
|
||||
func IsErrPullRequestHasMerged(err error) bool {
|
||||
_, ok := err.(ErrPullRequestHasMerged)
|
||||
return ok
|
||||
}
|
||||
|
||||
// Error does pretty-printing :D
|
||||
func (err ErrPullRequestHasMerged) Error() string {
|
||||
return fmt.Sprintf("pull request has merged [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]",
|
||||
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
|
||||
}
|
@ -13,9 +13,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/modules/testlogger"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -92,10 +92,7 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu
|
||||
func MainTest(m *testing.M) {
|
||||
testlogger.Init()
|
||||
|
||||
giteaRoot := base.SetupGiteaRoot()
|
||||
if giteaRoot == "" {
|
||||
testlogger.Fatalf("Environment variable $GITEA_ROOT not set\n")
|
||||
}
|
||||
giteaRoot := test.SetupGiteaRoot()
|
||||
giteaBinary := "gitea"
|
||||
if runtime.GOOS == "windows" {
|
||||
giteaBinary += ".exe"
|
||||
|
@ -9,11 +9,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
secret_model "code.gitea.io/gitea/models/secret"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
@ -407,33 +404,6 @@ func GetOrgByName(ctx context.Context, name string) (*Organization, error) {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// DeleteOrganization deletes models associated to an organization.
|
||||
func DeleteOrganization(ctx context.Context, org *Organization) error {
|
||||
if org.Type != user_model.UserTypeOrganization {
|
||||
return fmt.Errorf("%s is a user not an organization", org.Name)
|
||||
}
|
||||
|
||||
if err := db.DeleteBeans(ctx,
|
||||
&Team{OrgID: org.ID},
|
||||
&OrgUser{OrgID: org.ID},
|
||||
&TeamUser{OrgID: org.ID},
|
||||
&TeamUnit{OrgID: org.ID},
|
||||
&TeamInvite{OrgID: org.ID},
|
||||
&secret_model.Secret{OwnerID: org.ID},
|
||||
&user_model.Blocking{BlockerID: org.ID},
|
||||
&actions_model.ActionRunner{OwnerID: org.ID},
|
||||
&actions_model.ActionRunnerToken{OwnerID: org.ID},
|
||||
); err != nil {
|
||||
return fmt.Errorf("DeleteBeans: %w", err)
|
||||
}
|
||||
|
||||
if _, err := db.GetEngine(ctx).ID(org.ID).Delete(new(user_model.User)); err != nil {
|
||||
return fmt.Errorf("Delete: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetOrgUserMaxAuthorizeLevel returns highest authorize level of user in an organization
|
||||
func (org *Organization) GetOrgUserMaxAuthorizeLevel(ctx context.Context, uid int64) (perm.AccessMode, error) {
|
||||
var authorize perm.AccessMode
|
||||
@ -604,7 +574,9 @@ func RemoveOrgRepo(ctx context.Context, orgID, repoID int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (org *Organization) getUserTeams(ctx context.Context, userID int64, cols ...string) ([]*Team, error) {
|
||||
// GetUserTeams returns all teams that belong to user,
|
||||
// and that the user has joined.
|
||||
func (org *Organization) GetUserTeams(ctx context.Context, userID int64, cols ...string) ([]*Team, error) {
|
||||
teams := make([]*Team, 0, org.NumTeams)
|
||||
return teams, db.GetEngine(ctx).
|
||||
Where("`team_user`.org_id = ?", org.ID).
|
||||
@ -616,7 +588,8 @@ func (org *Organization) getUserTeams(ctx context.Context, userID int64, cols ..
|
||||
Find(&teams)
|
||||
}
|
||||
|
||||
func (org *Organization) getUserTeamIDs(ctx context.Context, userID int64) ([]int64, error) {
|
||||
// GetUserTeamIDs returns of all team IDs of the organization that user is member of.
|
||||
func (org *Organization) GetUserTeamIDs(ctx context.Context, userID int64) ([]int64, error) {
|
||||
teamIDs := make([]int64, 0, org.NumTeams)
|
||||
return teamIDs, db.GetEngine(ctx).
|
||||
Table("team").
|
||||
@ -640,175 +613,3 @@ func getUserTeamIDsQueryBuilder(orgID, userID int64) *builder.Builder {
|
||||
func (org *Organization) TeamsWithAccessToRepo(ctx context.Context, repoID int64, mode perm.AccessMode) ([]*Team, error) {
|
||||
return GetTeamsWithAccessToRepo(ctx, org.ID, repoID, mode)
|
||||
}
|
||||
|
||||
// GetUserTeamIDs returns of all team IDs of the organization that user is member of.
|
||||
func (org *Organization) GetUserTeamIDs(ctx context.Context, userID int64) ([]int64, error) {
|
||||
return org.getUserTeamIDs(ctx, userID)
|
||||
}
|
||||
|
||||
// GetUserTeams returns all teams that belong to user,
|
||||
// and that the user has joined.
|
||||
func (org *Organization) GetUserTeams(ctx context.Context, userID int64) ([]*Team, error) {
|
||||
return org.getUserTeams(ctx, userID)
|
||||
}
|
||||
|
||||
// AccessibleReposEnvironment operations involving the repositories that are
|
||||
// accessible to a particular user
|
||||
type AccessibleReposEnvironment interface {
|
||||
CountRepos() (int64, error)
|
||||
RepoIDs(page, pageSize int) ([]int64, error)
|
||||
Repos(page, pageSize int) (repo_model.RepositoryList, error)
|
||||
MirrorRepos() (repo_model.RepositoryList, error)
|
||||
AddKeyword(keyword string)
|
||||
SetSort(db.SearchOrderBy)
|
||||
}
|
||||
|
||||
type accessibleReposEnv struct {
|
||||
org *Organization
|
||||
user *user_model.User
|
||||
team *Team
|
||||
teamIDs []int64
|
||||
ctx context.Context
|
||||
keyword string
|
||||
orderBy db.SearchOrderBy
|
||||
}
|
||||
|
||||
// AccessibleReposEnv builds an AccessibleReposEnvironment for the repositories in `org`
|
||||
// that are accessible to the specified user.
|
||||
func AccessibleReposEnv(ctx context.Context, org *Organization, userID int64) (AccessibleReposEnvironment, error) {
|
||||
var user *user_model.User
|
||||
|
||||
if userID > 0 {
|
||||
u, err := user_model.GetUserByID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user = u
|
||||
}
|
||||
|
||||
teamIDs, err := org.getUserTeamIDs(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &accessibleReposEnv{
|
||||
org: org,
|
||||
user: user,
|
||||
teamIDs: teamIDs,
|
||||
ctx: ctx,
|
||||
orderBy: db.SearchOrderByRecentUpdated,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AccessibleTeamReposEnv an AccessibleReposEnvironment for the repositories in `org`
|
||||
// that are accessible to the specified team.
|
||||
func (org *Organization) AccessibleTeamReposEnv(ctx context.Context, team *Team) AccessibleReposEnvironment {
|
||||
return &accessibleReposEnv{
|
||||
org: org,
|
||||
team: team,
|
||||
ctx: ctx,
|
||||
orderBy: db.SearchOrderByRecentUpdated,
|
||||
}
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) cond() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if env.team != nil {
|
||||
cond = cond.And(builder.Eq{"team_repo.team_id": env.team.ID})
|
||||
} else {
|
||||
if env.user == nil || !env.user.IsRestricted {
|
||||
cond = cond.Or(builder.Eq{
|
||||
"`repository`.owner_id": env.org.ID,
|
||||
"`repository`.is_private": false,
|
||||
})
|
||||
}
|
||||
if len(env.teamIDs) > 0 {
|
||||
cond = cond.Or(builder.In("team_repo.team_id", env.teamIDs))
|
||||
}
|
||||
}
|
||||
if env.keyword != "" {
|
||||
cond = cond.And(builder.Like{"`repository`.lower_name", strings.ToLower(env.keyword)})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) CountRepos() (int64, error) {
|
||||
repoCount, err := db.GetEngine(env.ctx).
|
||||
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
|
||||
Where(env.cond()).
|
||||
Distinct("`repository`.id").
|
||||
Count(&repo_model.Repository{})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("count user repositories in organization: %w", err)
|
||||
}
|
||||
return repoCount, nil
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) RepoIDs(page, pageSize int) ([]int64, error) {
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
|
||||
repoIDs := make([]int64, 0, pageSize)
|
||||
return repoIDs, db.GetEngine(env.ctx).
|
||||
Table("repository").
|
||||
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
|
||||
Where(env.cond()).
|
||||
GroupBy("`repository`.id,`repository`."+strings.Fields(string(env.orderBy))[0]).
|
||||
OrderBy(string(env.orderBy)).
|
||||
Limit(pageSize, (page-1)*pageSize).
|
||||
Cols("`repository`.id").
|
||||
Find(&repoIDs)
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) Repos(page, pageSize int) (repo_model.RepositoryList, error) {
|
||||
repoIDs, err := env.RepoIDs(page, pageSize)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetUserRepositoryIDs: %w", err)
|
||||
}
|
||||
|
||||
repos := make([]*repo_model.Repository, 0, len(repoIDs))
|
||||
if len(repoIDs) == 0 {
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
return repos, db.GetEngine(env.ctx).
|
||||
In("`repository`.id", repoIDs).
|
||||
OrderBy(string(env.orderBy)).
|
||||
Find(&repos)
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) MirrorRepoIDs() ([]int64, error) {
|
||||
repoIDs := make([]int64, 0, 10)
|
||||
return repoIDs, db.GetEngine(env.ctx).
|
||||
Table("repository").
|
||||
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true).
|
||||
Where(env.cond()).
|
||||
GroupBy("`repository`.id, `repository`.updated_unix").
|
||||
OrderBy(string(env.orderBy)).
|
||||
Cols("`repository`.id").
|
||||
Find(&repoIDs)
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) MirrorRepos() (repo_model.RepositoryList, error) {
|
||||
repoIDs, err := env.MirrorRepoIDs()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("MirrorRepoIDs: %w", err)
|
||||
}
|
||||
|
||||
repos := make([]*repo_model.Repository, 0, len(repoIDs))
|
||||
if len(repoIDs) == 0 {
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
return repos, db.GetEngine(env.ctx).
|
||||
In("`repository`.id", repoIDs).
|
||||
Find(&repos)
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) AddKeyword(keyword string) {
|
||||
env.keyword = keyword
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) SetSort(orderBy db.SearchOrderBy) {
|
||||
env.orderBy = orderBy
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package organization
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
)
|
||||
|
||||
// GetOrgRepositories get repos belonging to the given organization
|
||||
func GetOrgRepositories(ctx context.Context, orgID int64) (repo_model.RepositoryList, error) {
|
||||
var orgRepos []*repo_model.Repository
|
||||
return orgRepos, db.GetEngine(ctx).Where("owner_id = ?", orgID).Find(&orgRepos)
|
||||
}
|
@ -318,7 +318,7 @@ func TestAccessibleReposEnv_CountRepos(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
|
||||
testSuccess := func(userID, expectedCount int64) {
|
||||
env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID)
|
||||
env, err := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID)
|
||||
assert.NoError(t, err)
|
||||
count, err := env.CountRepos()
|
||||
assert.NoError(t, err)
|
||||
@ -332,7 +332,7 @@ func TestAccessibleReposEnv_RepoIDs(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
|
||||
testSuccess := func(userID int64, expectedRepoIDs []int64) {
|
||||
env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID)
|
||||
env, err := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID)
|
||||
assert.NoError(t, err)
|
||||
repoIDs, err := env.RepoIDs(1, 100)
|
||||
assert.NoError(t, err)
|
||||
@ -346,7 +346,7 @@ func TestAccessibleReposEnv_Repos(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
|
||||
testSuccess := func(userID int64, expectedRepoIDs []int64) {
|
||||
env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID)
|
||||
env, err := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID)
|
||||
assert.NoError(t, err)
|
||||
repos, err := env.Repos(1, 100)
|
||||
assert.NoError(t, err)
|
||||
@ -365,7 +365,7 @@ func TestAccessibleReposEnv_MirrorRepos(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
|
||||
testSuccess := func(userID int64, expectedRepoIDs []int64) {
|
||||
env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID)
|
||||
env, err := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID)
|
||||
assert.NoError(t, err)
|
||||
repos, err := env.MirrorRepos()
|
||||
assert.NoError(t, err)
|
||||
|
@ -36,6 +36,21 @@ func init() {
|
||||
db.RegisterModel(new(OrgUser))
|
||||
}
|
||||
|
||||
// ErrUserHasOrgs represents a "UserHasOrgs" kind of error.
|
||||
type ErrUserHasOrgs struct {
|
||||
UID int64
|
||||
}
|
||||
|
||||
// IsErrUserHasOrgs checks if an error is a ErrUserHasOrgs.
|
||||
func IsErrUserHasOrgs(err error) bool {
|
||||
_, ok := err.(ErrUserHasOrgs)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUserHasOrgs) Error() string {
|
||||
return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID)
|
||||
}
|
||||
|
||||
// GetOrganizationCount returns count of membership of organization of the user.
|
||||
func GetOrganizationCount(ctx context.Context, u *user_model.User) (int64, error) {
|
||||
return db.GetEngine(ctx).
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
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/log"
|
||||
@ -78,9 +77,8 @@ type Team struct {
|
||||
LowerName string
|
||||
Name string
|
||||
Description string
|
||||
AccessMode perm.AccessMode `xorm:"'authorize'"`
|
||||
Repos []*repo_model.Repository `xorm:"-"`
|
||||
Members []*user_model.User `xorm:"-"`
|
||||
AccessMode perm.AccessMode `xorm:"'authorize'"`
|
||||
Members []*user_model.User `xorm:"-"`
|
||||
NumRepos int
|
||||
NumMembers int
|
||||
Units []*TeamUnit `xorm:"-"`
|
||||
@ -155,17 +153,6 @@ func (t *Team) IsMember(ctx context.Context, userID int64) bool {
|
||||
return isMember
|
||||
}
|
||||
|
||||
// LoadRepositories returns paginated repositories in team of organization.
|
||||
func (t *Team) LoadRepositories(ctx context.Context) (err error) {
|
||||
if t.Repos != nil {
|
||||
return nil
|
||||
}
|
||||
t.Repos, err = GetTeamRepositories(ctx, &SearchTeamRepoOptions{
|
||||
TeamID: t.ID,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// LoadMembers returns paginated members in team of organization.
|
||||
func (t *Team) LoadMembers(ctx context.Context) (err error) {
|
||||
t.Members, err = GetTeamMembers(ctx, &SearchMembersOptions{
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
|
||||
"xorm.io/builder"
|
||||
@ -98,11 +97,11 @@ func SearchTeam(ctx context.Context, opts *SearchTeamOptions) (TeamList, int64,
|
||||
}
|
||||
|
||||
// GetRepoTeams gets the list of teams that has access to the repository
|
||||
func GetRepoTeams(ctx context.Context, repo *repo_model.Repository) (teams TeamList, err error) {
|
||||
func GetRepoTeams(ctx context.Context, orgID, repoID int64) (teams TeamList, err error) {
|
||||
return teams, db.GetEngine(ctx).
|
||||
Join("INNER", "team_repo", "team_repo.team_id = team.id").
|
||||
Where("team.org_id = ?", repo.OwnerID).
|
||||
And("team_repo.repo_id=?", repo.ID).
|
||||
Where("team.org_id = ?", orgID).
|
||||
And("team_repo.repo_id=?", repoID).
|
||||
OrderBy("CASE WHEN name LIKE '" + OwnerTeamName + "' THEN '' ELSE name END").
|
||||
Find(&teams)
|
||||
}
|
||||
|
@ -8,10 +8,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// TeamRepo represents an team-repository relation.
|
||||
@ -32,29 +29,6 @@ func HasTeamRepo(ctx context.Context, orgID, teamID, repoID int64) bool {
|
||||
return has
|
||||
}
|
||||
|
||||
type SearchTeamRepoOptions struct {
|
||||
db.ListOptions
|
||||
TeamID int64
|
||||
}
|
||||
|
||||
// GetRepositories returns paginated repositories in team of organization.
|
||||
func GetTeamRepositories(ctx context.Context, opts *SearchTeamRepoOptions) (repo_model.RepositoryList, error) {
|
||||
sess := db.GetEngine(ctx)
|
||||
if opts.TeamID > 0 {
|
||||
sess = sess.In("id",
|
||||
builder.Select("repo_id").
|
||||
From("team_repo").
|
||||
Where(builder.Eq{"team_id": opts.TeamID}),
|
||||
)
|
||||
}
|
||||
if opts.PageSize > 0 {
|
||||
sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
|
||||
}
|
||||
var repos []*repo_model.Repository
|
||||
return repos, sess.OrderBy("repository.name").
|
||||
Find(&repos)
|
||||
}
|
||||
|
||||
// AddTeamRepo adds a repo for an organization's team
|
||||
func AddTeamRepo(ctx context.Context, orgID, teamID, repoID int64) error {
|
||||
_, err := db.GetEngine(ctx).Insert(&TeamRepo{
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -42,9 +43,12 @@ func TestTeam_GetRepositories(t *testing.T) {
|
||||
|
||||
test := func(teamID int64) {
|
||||
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
|
||||
assert.NoError(t, team.LoadRepositories(db.DefaultContext))
|
||||
assert.Len(t, team.Repos, team.NumRepos)
|
||||
for _, repo := range team.Repos {
|
||||
repos, err := repo_model.GetTeamRepositories(db.DefaultContext, &repo_model.SearchTeamRepoOptions{
|
||||
TeamID: team.ID,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, repos, team.NumRepos)
|
||||
for _, repo := range repos {
|
||||
unittest.AssertExistsAndLoadBean(t, &organization.TeamRepo{TeamID: teamID, RepoID: repo.ID})
|
||||
}
|
||||
}
|
||||
|
@ -301,6 +301,21 @@ func FindUnreferencedPackages(ctx context.Context) ([]*Package, error) {
|
||||
Find(&ps)
|
||||
}
|
||||
|
||||
// ErrUserOwnPackages notifies that the user (still) owns the packages.
|
||||
type ErrUserOwnPackages struct {
|
||||
UID int64
|
||||
}
|
||||
|
||||
// IsErrUserOwnPackages checks if an error is an ErrUserOwnPackages.
|
||||
func IsErrUserOwnPackages(err error) bool {
|
||||
_, ok := err.(ErrUserOwnPackages)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUserOwnPackages) Error() string {
|
||||
return fmt.Sprintf("user still has ownership of packages [uid: %d]", err.UID)
|
||||
}
|
||||
|
||||
// HasOwnerPackages tests if a user/org has accessible packages
|
||||
func HasOwnerPackages(ctx context.Context, ownerID int64) (bool, error) {
|
||||
return db.GetEngine(ctx).
|
||||
|
@ -6,15 +6,12 @@ package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
_ "image/jpeg" // Needed for jpeg support
|
||||
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
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"
|
||||
@ -315,48 +312,3 @@ func DoctorUserStarNum(ctx context.Context) (err error) {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteDeployKey delete deploy keys
|
||||
func DeleteDeployKey(ctx context.Context, doer *user_model.User, id int64) error {
|
||||
key, err := asymkey_model.GetDeployKeyByID(ctx, id)
|
||||
if err != nil {
|
||||
if asymkey_model.IsErrDeployKeyNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("GetDeployKeyByID: %w", err)
|
||||
}
|
||||
|
||||
// Check if user has access to delete this key.
|
||||
if !doer.IsAdmin {
|
||||
repo, err := repo_model.GetRepositoryByID(ctx, key.RepoID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetRepositoryByID: %w", err)
|
||||
}
|
||||
has, err := access_model.IsUserRepoAdmin(ctx, repo, doer)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetUserRepoPermission: %w", err)
|
||||
} else if !has {
|
||||
return asymkey_model.ErrKeyAccessDenied{
|
||||
UserID: doer.ID,
|
||||
KeyID: key.ID,
|
||||
Note: "deploy",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := db.DeleteByID[asymkey_model.DeployKey](ctx, key.ID); err != nil {
|
||||
return fmt.Errorf("delete deploy key [%d]: %w", key.ID, err)
|
||||
}
|
||||
|
||||
// Check if this is the last reference to same key content.
|
||||
has, err := asymkey_model.IsDeployKeyExistByKeyID(ctx, key.KeyID)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !has {
|
||||
if _, err = db.DeleteByID[asymkey_model.PublicKey](ctx, key.KeyID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
206
models/repo/org_repo.go
Normal file
206
models/repo/org_repo.go
Normal file
@ -0,0 +1,206 @@
|
||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
org_model "code.gitea.io/gitea/models/organization"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// GetOrgRepositories get repos belonging to the given organization
|
||||
func GetOrgRepositories(ctx context.Context, orgID int64) (RepositoryList, error) {
|
||||
var orgRepos []*Repository
|
||||
return orgRepos, db.GetEngine(ctx).Where("owner_id = ?", orgID).Find(&orgRepos)
|
||||
}
|
||||
|
||||
type SearchTeamRepoOptions struct {
|
||||
db.ListOptions
|
||||
TeamID int64
|
||||
}
|
||||
|
||||
// GetRepositories returns paginated repositories in team of organization.
|
||||
func GetTeamRepositories(ctx context.Context, opts *SearchTeamRepoOptions) (RepositoryList, error) {
|
||||
sess := db.GetEngine(ctx)
|
||||
if opts.TeamID > 0 {
|
||||
sess = sess.In("id",
|
||||
builder.Select("repo_id").
|
||||
From("team_repo").
|
||||
Where(builder.Eq{"team_id": opts.TeamID}),
|
||||
)
|
||||
}
|
||||
if opts.PageSize > 0 {
|
||||
sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
|
||||
}
|
||||
var repos []*Repository
|
||||
return repos, sess.OrderBy("repository.name").
|
||||
Find(&repos)
|
||||
}
|
||||
|
||||
// AccessibleReposEnvironment operations involving the repositories that are
|
||||
// accessible to a particular user
|
||||
type AccessibleReposEnvironment interface {
|
||||
CountRepos() (int64, error)
|
||||
RepoIDs(page, pageSize int) ([]int64, error)
|
||||
Repos(page, pageSize int) (RepositoryList, error)
|
||||
MirrorRepos() (RepositoryList, error)
|
||||
AddKeyword(keyword string)
|
||||
SetSort(db.SearchOrderBy)
|
||||
}
|
||||
|
||||
type accessibleReposEnv struct {
|
||||
org *org_model.Organization
|
||||
user *user_model.User
|
||||
team *org_model.Team
|
||||
teamIDs []int64
|
||||
ctx context.Context
|
||||
keyword string
|
||||
orderBy db.SearchOrderBy
|
||||
}
|
||||
|
||||
// AccessibleReposEnv builds an AccessibleReposEnvironment for the repositories in `org`
|
||||
// that are accessible to the specified user.
|
||||
func AccessibleReposEnv(ctx context.Context, org *org_model.Organization, userID int64) (AccessibleReposEnvironment, error) {
|
||||
var user *user_model.User
|
||||
|
||||
if userID > 0 {
|
||||
u, err := user_model.GetUserByID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user = u
|
||||
}
|
||||
|
||||
teamIDs, err := org.GetUserTeamIDs(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &accessibleReposEnv{
|
||||
org: org,
|
||||
user: user,
|
||||
teamIDs: teamIDs,
|
||||
ctx: ctx,
|
||||
orderBy: db.SearchOrderByRecentUpdated,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AccessibleTeamReposEnv an AccessibleReposEnvironment for the repositories in `org`
|
||||
// that are accessible to the specified team.
|
||||
func AccessibleTeamReposEnv(ctx context.Context, org *org_model.Organization, team *org_model.Team) AccessibleReposEnvironment {
|
||||
return &accessibleReposEnv{
|
||||
org: org,
|
||||
team: team,
|
||||
ctx: ctx,
|
||||
orderBy: db.SearchOrderByRecentUpdated,
|
||||
}
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) cond() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if env.team != nil {
|
||||
cond = cond.And(builder.Eq{"team_repo.team_id": env.team.ID})
|
||||
} else {
|
||||
if env.user == nil || !env.user.IsRestricted {
|
||||
cond = cond.Or(builder.Eq{
|
||||
"`repository`.owner_id": env.org.ID,
|
||||
"`repository`.is_private": false,
|
||||
})
|
||||
}
|
||||
if len(env.teamIDs) > 0 {
|
||||
cond = cond.Or(builder.In("team_repo.team_id", env.teamIDs))
|
||||
}
|
||||
}
|
||||
if env.keyword != "" {
|
||||
cond = cond.And(builder.Like{"`repository`.lower_name", strings.ToLower(env.keyword)})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) CountRepos() (int64, error) {
|
||||
repoCount, err := db.GetEngine(env.ctx).
|
||||
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
|
||||
Where(env.cond()).
|
||||
Distinct("`repository`.id").
|
||||
Count(&Repository{})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("count user repositories in organization: %w", err)
|
||||
}
|
||||
return repoCount, nil
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) RepoIDs(page, pageSize int) ([]int64, error) {
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
|
||||
repoIDs := make([]int64, 0, pageSize)
|
||||
return repoIDs, db.GetEngine(env.ctx).
|
||||
Table("repository").
|
||||
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
|
||||
Where(env.cond()).
|
||||
GroupBy("`repository`.id,`repository`."+strings.Fields(string(env.orderBy))[0]).
|
||||
OrderBy(string(env.orderBy)).
|
||||
Limit(pageSize, (page-1)*pageSize).
|
||||
Cols("`repository`.id").
|
||||
Find(&repoIDs)
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) Repos(page, pageSize int) (RepositoryList, error) {
|
||||
repoIDs, err := env.RepoIDs(page, pageSize)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetUserRepositoryIDs: %w", err)
|
||||
}
|
||||
|
||||
repos := make([]*Repository, 0, len(repoIDs))
|
||||
if len(repoIDs) == 0 {
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
return repos, db.GetEngine(env.ctx).
|
||||
In("`repository`.id", repoIDs).
|
||||
OrderBy(string(env.orderBy)).
|
||||
Find(&repos)
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) MirrorRepoIDs() ([]int64, error) {
|
||||
repoIDs := make([]int64, 0, 10)
|
||||
return repoIDs, db.GetEngine(env.ctx).
|
||||
Table("repository").
|
||||
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true).
|
||||
Where(env.cond()).
|
||||
GroupBy("`repository`.id, `repository`.updated_unix").
|
||||
OrderBy(string(env.orderBy)).
|
||||
Cols("`repository`.id").
|
||||
Find(&repoIDs)
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) MirrorRepos() (RepositoryList, error) {
|
||||
repoIDs, err := env.MirrorRepoIDs()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("MirrorRepoIDs: %w", err)
|
||||
}
|
||||
|
||||
repos := make([]*Repository, 0, len(repoIDs))
|
||||
if len(repoIDs) == 0 {
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
return repos, db.GetEngine(env.ctx).
|
||||
In("`repository`.id", repoIDs).
|
||||
Find(&repos)
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) AddKeyword(keyword string) {
|
||||
env.keyword = keyword
|
||||
}
|
||||
|
||||
func (env *accessibleReposEnv) SetSort(orderBy db.SearchOrderBy) {
|
||||
env.orderBy = orderBy
|
||||
}
|
@ -37,7 +37,7 @@ type ErrUserDoesNotHaveAccessToRepo struct {
|
||||
RepoName string
|
||||
}
|
||||
|
||||
// IsErrUserDoesNotHaveAccessToRepo checks if an error is a ErrRepoFileAlreadyExists.
|
||||
// IsErrUserDoesNotHaveAccessToRepo checks if an error is a ErrUserDoesNotHaveAccessToRepo.
|
||||
func IsErrUserDoesNotHaveAccessToRepo(err error) bool {
|
||||
_, ok := err.(ErrUserDoesNotHaveAccessToRepo)
|
||||
return ok
|
||||
@ -866,6 +866,21 @@ func (repo *Repository) TemplateRepo(ctx context.Context) *Repository {
|
||||
return repo
|
||||
}
|
||||
|
||||
// ErrUserOwnRepos represents a "UserOwnRepos" kind of error.
|
||||
type ErrUserOwnRepos struct {
|
||||
UID int64
|
||||
}
|
||||
|
||||
// IsErrUserOwnRepos checks if an error is a ErrUserOwnRepos.
|
||||
func IsErrUserOwnRepos(err error) bool {
|
||||
_, ok := err.(ErrUserOwnRepos)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUserOwnRepos) Error() string {
|
||||
return fmt.Sprintf("user still has ownership of repositories [uid: %d]", err.UID)
|
||||
}
|
||||
|
||||
type CountRepositoryOptions struct {
|
||||
OwnerID int64
|
||||
Private optional.Option[bool]
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package models
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -10,16 +10,58 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// ErrNoPendingRepoTransfer is an error type for repositories without a pending
|
||||
// transfer request
|
||||
type ErrNoPendingRepoTransfer struct {
|
||||
RepoID int64
|
||||
}
|
||||
|
||||
func (err ErrNoPendingRepoTransfer) Error() string {
|
||||
return fmt.Sprintf("repository doesn't have a pending transfer [repo_id: %d]", err.RepoID)
|
||||
}
|
||||
|
||||
// IsErrNoPendingTransfer is an error type when a repository has no pending
|
||||
// transfers
|
||||
func IsErrNoPendingTransfer(err error) bool {
|
||||
_, ok := err.(ErrNoPendingRepoTransfer)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrNoPendingRepoTransfer) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrRepoTransferInProgress represents the state of a repository that has an
|
||||
// ongoing transfer
|
||||
type ErrRepoTransferInProgress struct {
|
||||
Uname string
|
||||
Name string
|
||||
}
|
||||
|
||||
// IsErrRepoTransferInProgress checks if an error is a ErrRepoTransferInProgress.
|
||||
func IsErrRepoTransferInProgress(err error) bool {
|
||||
_, ok := err.(ErrRepoTransferInProgress)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrRepoTransferInProgress) Error() string {
|
||||
return fmt.Sprintf("repository is already being transferred [uname: %s, name: %s]", err.Uname, err.Name)
|
||||
}
|
||||
|
||||
func (err ErrRepoTransferInProgress) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// RepoTransfer is used to manage repository transfers
|
||||
type RepoTransfer struct {
|
||||
type RepoTransfer struct { //nolint
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
DoerID int64
|
||||
Doer *user_model.User `xorm:"-"`
|
||||
@ -126,7 +168,7 @@ func GetPendingRepositoryTransfers(ctx context.Context, opts *PendingRepositoryT
|
||||
|
||||
// GetPendingRepositoryTransfer fetches the most recent and ongoing transfer
|
||||
// process for the repository
|
||||
func GetPendingRepositoryTransfer(ctx context.Context, repo *repo_model.Repository) (*RepoTransfer, error) {
|
||||
func GetPendingRepositoryTransfer(ctx context.Context, repo *Repository) (*RepoTransfer, error) {
|
||||
transfers, err := GetPendingRepositoryTransfers(ctx, &PendingRepositoryTransferOptions{RepoID: repo.ID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -145,11 +187,11 @@ func DeleteRepositoryTransfer(ctx context.Context, repoID int64) error {
|
||||
}
|
||||
|
||||
// TestRepositoryReadyForTransfer make sure repo is ready to transfer
|
||||
func TestRepositoryReadyForTransfer(status repo_model.RepositoryStatus) error {
|
||||
func TestRepositoryReadyForTransfer(status RepositoryStatus) error {
|
||||
switch status {
|
||||
case repo_model.RepositoryBeingMigrated:
|
||||
case RepositoryBeingMigrated:
|
||||
return errors.New("repo is not ready, currently migrating")
|
||||
case repo_model.RepositoryPendingTransfer:
|
||||
case RepositoryPendingTransfer:
|
||||
return ErrRepoTransferInProgress{}
|
||||
}
|
||||
return nil
|
||||
@ -159,7 +201,7 @@ func TestRepositoryReadyForTransfer(status repo_model.RepositoryStatus) error {
|
||||
// it marks the repository transfer as "pending"
|
||||
func CreatePendingRepositoryTransfer(ctx context.Context, doer, newOwner *user_model.User, repoID int64, teams []*organization.Team) error {
|
||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||
repo, err := repo_model.GetRepositoryByID(ctx, repoID)
|
||||
repo, err := GetRepositoryByID(ctx, repoID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -169,16 +211,16 @@ func CreatePendingRepositoryTransfer(ctx context.Context, doer, newOwner *user_m
|
||||
return err
|
||||
}
|
||||
|
||||
repo.Status = repo_model.RepositoryPendingTransfer
|
||||
if err := repo_model.UpdateRepositoryCols(ctx, repo, "status"); err != nil {
|
||||
repo.Status = RepositoryPendingTransfer
|
||||
if err := UpdateRepositoryCols(ctx, repo, "status"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if new owner has repository with same name.
|
||||
if has, err := repo_model.IsRepositoryModelExist(ctx, newOwner, repo.Name); err != nil {
|
||||
if has, err := IsRepositoryModelExist(ctx, newOwner, repo.Name); err != nil {
|
||||
return fmt.Errorf("IsRepositoryExist: %w", err)
|
||||
} else if has {
|
||||
return repo_model.ErrRepoAlreadyExist{
|
||||
return ErrRepoAlreadyExist{
|
||||
Uname: newOwner.LowerName,
|
||||
Name: repo.Name,
|
||||
}
|
@ -14,13 +14,13 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/system"
|
||||
"code.gitea.io/gitea/modules/auth/password/hash"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/setting/config"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -235,5 +235,5 @@ func PrepareTestEnv(t testing.TB) {
|
||||
assert.NoError(t, PrepareTestDatabase())
|
||||
metaPath := filepath.Join(giteaRoot, "tests", "gitea-repositories-meta")
|
||||
assert.NoError(t, SyncDirs(metaPath, setting.RepoRootPath))
|
||||
base.SetupGiteaRoot() // Makes sure GITEA_ROOT is set
|
||||
test.SetupGiteaRoot() // Makes sure GITEA_ROOT is set
|
||||
}
|
||||
|
@ -788,6 +788,21 @@ func createUser(ctx context.Context, u *User, meta *Meta, createdByAdmin bool, o
|
||||
return committer.Commit()
|
||||
}
|
||||
|
||||
// ErrDeleteLastAdminUser represents a "DeleteLastAdminUser" kind of error.
|
||||
type ErrDeleteLastAdminUser struct {
|
||||
UID int64
|
||||
}
|
||||
|
||||
// IsErrDeleteLastAdminUser checks if an error is a ErrDeleteLastAdminUser.
|
||||
func IsErrDeleteLastAdminUser(err error) bool {
|
||||
_, ok := err.(ErrDeleteLastAdminUser)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrDeleteLastAdminUser) Error() string {
|
||||
return fmt.Sprintf("can not delete the last admin user [uid: %d]", err.UID)
|
||||
}
|
||||
|
||||
// IsLastAdminUser check whether user is the last admin
|
||||
func IsLastAdminUser(ctx context.Context, user *User) bool {
|
||||
if user.IsAdmin && CountUsers(ctx, &CountUserFilter{IsAdmin: optional.Some(true)}) <= 1 {
|
||||
|
@ -1,9 +0,0 @@
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package base
|
||||
|
||||
type (
|
||||
// TplName template relative path type
|
||||
TplName string
|
||||
)
|
@ -13,9 +13,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -189,49 +186,3 @@ func EntryIcon(entry *git.TreeEntry) string {
|
||||
|
||||
return "file"
|
||||
}
|
||||
|
||||
// SetupGiteaRoot Sets GITEA_ROOT if it is not already set and returns the value
|
||||
func SetupGiteaRoot() string {
|
||||
giteaRoot := os.Getenv("GITEA_ROOT")
|
||||
if giteaRoot == "" {
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
giteaRoot = strings.TrimSuffix(filename, "modules/base/tool.go")
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
rel, err := filepath.Rel(giteaRoot, wd)
|
||||
if err != nil && strings.HasPrefix(filepath.ToSlash(rel), "../") {
|
||||
giteaRoot = wd
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(giteaRoot, "gitea")); os.IsNotExist(err) {
|
||||
giteaRoot = ""
|
||||
} else if err := os.Setenv("GITEA_ROOT", giteaRoot); err != nil {
|
||||
giteaRoot = ""
|
||||
}
|
||||
}
|
||||
return giteaRoot
|
||||
}
|
||||
|
||||
// FormatNumberSI format a number
|
||||
func FormatNumberSI(data any) string {
|
||||
var num int64
|
||||
if num1, ok := data.(int64); ok {
|
||||
num = num1
|
||||
} else if num1, ok := data.(int); ok {
|
||||
num = int64(num1)
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
|
||||
if num < 1000 {
|
||||
return fmt.Sprintf("%d", num)
|
||||
} else if num < 1000000 {
|
||||
num2 := float32(num) / float32(1000.0)
|
||||
return fmt.Sprintf("%.1fk", num2)
|
||||
} else if num < 1000000000 {
|
||||
num2 := float32(num) / float32(1000000.0)
|
||||
return fmt.Sprintf("%.1fM", num2)
|
||||
}
|
||||
num2 := float32(num) / float32(1000000000.0)
|
||||
return fmt.Sprintf("%.1fG", num2)
|
||||
}
|
||||
|
@ -169,18 +169,3 @@ func TestInt64sToStrings(t *testing.T) {
|
||||
}
|
||||
|
||||
// TODO: Test EntryIcon
|
||||
|
||||
func TestSetupGiteaRoot(t *testing.T) {
|
||||
t.Setenv("GITEA_ROOT", "test")
|
||||
assert.Equal(t, "test", SetupGiteaRoot())
|
||||
t.Setenv("GITEA_ROOT", "")
|
||||
assert.NotEqual(t, "test", SetupGiteaRoot())
|
||||
}
|
||||
|
||||
func TestFormatNumberSI(t *testing.T) {
|
||||
assert.Equal(t, "125", FormatNumberSI(int(125)))
|
||||
assert.Equal(t, "1.3k", FormatNumberSI(int64(1317)))
|
||||
assert.Equal(t, "21.3M", FormatNumberSI(21317675))
|
||||
assert.Equal(t, "45.7G", FormatNumberSI(45721317675))
|
||||
assert.Equal(t, "", FormatNumberSI("test"))
|
||||
}
|
||||
|
@ -7,10 +7,8 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -32,7 +30,6 @@ type WriteCloserError interface {
|
||||
func ensureValidGitRepository(ctx context.Context, repoPath string) error {
|
||||
stderr := strings.Builder{}
|
||||
err := NewCommand(ctx, "rev-parse").
|
||||
SetDescription(fmt.Sprintf("%s rev-parse [repo_path: %s]", GitExecutable, repoPath)).
|
||||
Run(&RunOpts{
|
||||
Dir: repoPath,
|
||||
Stderr: &stderr,
|
||||
@ -62,13 +59,9 @@ func catFileBatchCheck(ctx context.Context, repoPath string) (WriteCloserError,
|
||||
cancel()
|
||||
}()
|
||||
|
||||
_, filename, line, _ := runtime.Caller(2)
|
||||
filename = strings.TrimPrefix(filename, callerPrefix)
|
||||
|
||||
go func() {
|
||||
stderr := strings.Builder{}
|
||||
err := NewCommand(ctx, "cat-file", "--batch-check").
|
||||
SetDescription(fmt.Sprintf("%s cat-file --batch-check [repo_path: %s] (%s:%d)", GitExecutable, repoPath, filename, line)).
|
||||
Run(&RunOpts{
|
||||
Dir: repoPath,
|
||||
Stdin: batchStdinReader,
|
||||
@ -114,13 +107,9 @@ func catFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi
|
||||
cancel()
|
||||
}()
|
||||
|
||||
_, filename, line, _ := runtime.Caller(2)
|
||||
filename = strings.TrimPrefix(filename, callerPrefix)
|
||||
|
||||
go func() {
|
||||
stderr := strings.Builder{}
|
||||
err := NewCommand(ctx, "cat-file", "--batch").
|
||||
SetDescription(fmt.Sprintf("%s cat-file --batch [repo_path: %s] (%s:%d)", GitExecutable, repoPath, filename, line)).
|
||||
Run(&RunOpts{
|
||||
Dir: repoPath,
|
||||
Stdin: batchStdinReader,
|
||||
@ -320,13 +309,6 @@ func ParseTreeLine(objectFormat ObjectFormat, rd *bufio.Reader, modeBuf, fnameBu
|
||||
return mode, fname, sha, n, err
|
||||
}
|
||||
|
||||
var callerPrefix string
|
||||
|
||||
func init() {
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
callerPrefix = strings.TrimSuffix(filename, "modules/git/batch_reader.go")
|
||||
}
|
||||
|
||||
func DiscardFull(rd *bufio.Reader, discard int64) error {
|
||||
if discard > math.MaxInt32 {
|
||||
n, err := rd.Discard(math.MaxInt32)
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
@ -142,9 +141,7 @@ func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath
|
||||
// There is no equivalent on Windows. May be implemented if Gitea uses an external git backend.
|
||||
cmd.AddOptionValues("--ignore-revs-file", *ignoreRevsFile)
|
||||
}
|
||||
cmd.AddDynamicArguments(commit.ID.String()).
|
||||
AddDashesAndList(file).
|
||||
SetDescription(fmt.Sprintf("GetBlame [repo_path: %s]", repoPath))
|
||||
cmd.AddDynamicArguments(commit.ID.String()).AddDashesAndList(file)
|
||||
reader, stdout, err := os.Pipe()
|
||||
if err != nil {
|
||||
if ignoreRevsFile != nil {
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
@ -43,18 +44,24 @@ type Command struct {
|
||||
prog string
|
||||
args []string
|
||||
parentContext context.Context
|
||||
desc string
|
||||
globalArgsLength int
|
||||
brokenArgs []string
|
||||
}
|
||||
|
||||
func (c *Command) String() string {
|
||||
return c.toString(false)
|
||||
func logArgSanitize(arg string) string {
|
||||
if strings.Contains(arg, "://") && strings.Contains(arg, "@") {
|
||||
return util.SanitizeCredentialURLs(arg)
|
||||
} else if filepath.IsAbs(arg) {
|
||||
base := filepath.Base(arg)
|
||||
dir := filepath.Dir(arg)
|
||||
return filepath.Join(filepath.Base(dir), base)
|
||||
}
|
||||
return arg
|
||||
}
|
||||
|
||||
func (c *Command) toString(sanitizing bool) string {
|
||||
func (c *Command) LogString() string {
|
||||
// WARNING: this function is for debugging purposes only. It's much better than old code (which only joins args with space),
|
||||
// It's impossible to make a simple and 100% correct implementation of argument quoting for different platforms.
|
||||
// It's impossible to make a simple and 100% correct implementation of argument quoting for different platforms here.
|
||||
debugQuote := func(s string) string {
|
||||
if strings.ContainsAny(s, " `'\"\t\r\n") {
|
||||
return fmt.Sprintf("%q", s)
|
||||
@ -63,12 +70,11 @@ func (c *Command) toString(sanitizing bool) string {
|
||||
}
|
||||
a := make([]string, 0, len(c.args)+1)
|
||||
a = append(a, debugQuote(c.prog))
|
||||
for _, arg := range c.args {
|
||||
if sanitizing && (strings.Contains(arg, "://") && strings.Contains(arg, "@")) {
|
||||
a = append(a, debugQuote(util.SanitizeCredentialURLs(arg)))
|
||||
} else {
|
||||
a = append(a, debugQuote(arg))
|
||||
}
|
||||
if c.globalArgsLength > 0 {
|
||||
a = append(a, "...global...")
|
||||
}
|
||||
for i := c.globalArgsLength; i < len(c.args); i++ {
|
||||
a = append(a, debugQuote(logArgSanitize(c.args[i])))
|
||||
}
|
||||
return strings.Join(a, " ")
|
||||
}
|
||||
@ -112,12 +118,6 @@ func (c *Command) SetParentContext(ctx context.Context) *Command {
|
||||
return c
|
||||
}
|
||||
|
||||
// SetDescription sets the description for this command which be returned on c.String()
|
||||
func (c *Command) SetDescription(desc string) *Command {
|
||||
c.desc = desc
|
||||
return c
|
||||
}
|
||||
|
||||
// isSafeArgumentValue checks if the argument is safe to be used as a value (not an option)
|
||||
func isSafeArgumentValue(s string) bool {
|
||||
return s == "" || s[0] != '-'
|
||||
@ -271,8 +271,12 @@ var ErrBrokenCommand = errors.New("git command is broken")
|
||||
|
||||
// Run runs the command with the RunOpts
|
||||
func (c *Command) Run(opts *RunOpts) error {
|
||||
return c.run(1, opts)
|
||||
}
|
||||
|
||||
func (c *Command) run(skip int, opts *RunOpts) error {
|
||||
if len(c.brokenArgs) != 0 {
|
||||
log.Error("git command is broken: %s, broken args: %s", c.String(), strings.Join(c.brokenArgs, " "))
|
||||
log.Error("git command is broken: %s, broken args: %s", c.LogString(), strings.Join(c.brokenArgs, " "))
|
||||
return ErrBrokenCommand
|
||||
}
|
||||
if opts == nil {
|
||||
@ -285,20 +289,14 @@ func (c *Command) Run(opts *RunOpts) error {
|
||||
timeout = defaultCommandExecutionTimeout
|
||||
}
|
||||
|
||||
if len(opts.Dir) == 0 {
|
||||
log.Debug("git.Command.Run: %s", c)
|
||||
} else {
|
||||
log.Debug("git.Command.RunDir(%s): %s", opts.Dir, c)
|
||||
}
|
||||
|
||||
desc := c.desc
|
||||
if desc == "" {
|
||||
if opts.Dir == "" {
|
||||
desc = fmt.Sprintf("git: %s", c.toString(true))
|
||||
} else {
|
||||
desc = fmt.Sprintf("git(dir:%s): %s", opts.Dir, c.toString(true))
|
||||
}
|
||||
var desc string
|
||||
callerInfo := util.CallerFuncName(1 /* util */ + 1 /* this */ + skip /* parent */)
|
||||
if pos := strings.LastIndex(callerInfo, "/"); pos >= 0 {
|
||||
callerInfo = callerInfo[pos+1:]
|
||||
}
|
||||
// these logs are for debugging purposes only, so no guarantee of correctness or stability
|
||||
desc = fmt.Sprintf("git.Run(by:%s, repo:%s): %s", callerInfo, logArgSanitize(opts.Dir), c.LogString())
|
||||
log.Debug("git.Command: %s", desc)
|
||||
|
||||
var ctx context.Context
|
||||
var cancel context.CancelFunc
|
||||
@ -401,7 +399,7 @@ func IsErrorExitCode(err error, code int) bool {
|
||||
|
||||
// RunStdString runs the command with options and returns stdout/stderr as string. and store stderr to returned error (err combined with stderr).
|
||||
func (c *Command) RunStdString(opts *RunOpts) (stdout, stderr string, runErr RunStdError) {
|
||||
stdoutBytes, stderrBytes, err := c.RunStdBytes(opts)
|
||||
stdoutBytes, stderrBytes, err := c.runStdBytes(opts)
|
||||
stdout = util.UnsafeBytesToString(stdoutBytes)
|
||||
stderr = util.UnsafeBytesToString(stderrBytes)
|
||||
if err != nil {
|
||||
@ -413,6 +411,10 @@ func (c *Command) RunStdString(opts *RunOpts) (stdout, stderr string, runErr Run
|
||||
|
||||
// RunStdBytes runs the command with options and returns stdout/stderr as bytes. and store stderr to returned error (err combined with stderr).
|
||||
func (c *Command) RunStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunStdError) {
|
||||
return c.runStdBytes(opts)
|
||||
}
|
||||
|
||||
func (c *Command) runStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunStdError) {
|
||||
if opts == nil {
|
||||
opts = &RunOpts{}
|
||||
}
|
||||
@ -435,7 +437,7 @@ func (c *Command) RunStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunS
|
||||
PipelineFunc: opts.PipelineFunc,
|
||||
}
|
||||
|
||||
err := c.Run(newOpts)
|
||||
err := c.run(2, newOpts)
|
||||
stderr = stderrBuf.Bytes()
|
||||
if err != nil {
|
||||
return nil, stderr, &runStdError{err: err, stderr: util.UnsafeBytesToString(stderr)}
|
||||
|
@ -55,8 +55,8 @@ func TestGitArgument(t *testing.T) {
|
||||
|
||||
func TestCommandString(t *testing.T) {
|
||||
cmd := NewCommandContextNoGlobals(context.Background(), "a", "-m msg", "it's a test", `say "hello"`)
|
||||
assert.EqualValues(t, cmd.prog+` a "-m msg" "it's a test" "say \"hello\""`, cmd.String())
|
||||
assert.EqualValues(t, cmd.prog+` a "-m msg" "it's a test" "say \"hello\""`, cmd.LogString())
|
||||
|
||||
cmd = NewCommandContextNoGlobals(context.Background(), "url: https://a:b@c/")
|
||||
assert.EqualValues(t, cmd.prog+` "url: https://sanitized-credential@c/"`, cmd.toString(true))
|
||||
cmd = NewCommandContextNoGlobals(context.Background(), "url: https://a:b@c/", "/root/dir-a/dir-b")
|
||||
assert.EqualValues(t, cmd.prog+` "url: https://sanitized-credential@c/" dir-a/dir-b`, cmd.LogString())
|
||||
}
|
||||
|
@ -5,8 +5,12 @@ package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
giturl "code.gitea.io/gitea/modules/git/url"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// GetRemoteAddress returns remote url of git repository in the repoPath with special remote name
|
||||
@ -37,3 +41,61 @@ func GetRemoteURL(ctx context.Context, repoPath, remoteName string) (*giturl.Git
|
||||
}
|
||||
return giturl.Parse(addr)
|
||||
}
|
||||
|
||||
// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
|
||||
type ErrInvalidCloneAddr struct {
|
||||
Host string
|
||||
IsURLError bool
|
||||
IsInvalidPath bool
|
||||
IsProtocolInvalid bool
|
||||
IsPermissionDenied bool
|
||||
LocalPath bool
|
||||
}
|
||||
|
||||
// IsErrInvalidCloneAddr checks if an error is a ErrInvalidCloneAddr.
|
||||
func IsErrInvalidCloneAddr(err error) bool {
|
||||
_, ok := err.(*ErrInvalidCloneAddr)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err *ErrInvalidCloneAddr) Error() string {
|
||||
if err.IsInvalidPath {
|
||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided path is invalid", err.Host)
|
||||
}
|
||||
if err.IsProtocolInvalid {
|
||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url protocol is not allowed", err.Host)
|
||||
}
|
||||
if err.IsPermissionDenied {
|
||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed.", err.Host)
|
||||
}
|
||||
if err.IsURLError {
|
||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url is invalid", err.Host)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("migration/cloning from '%s' is not allowed", err.Host)
|
||||
}
|
||||
|
||||
func (err *ErrInvalidCloneAddr) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// ParseRemoteAddr checks if given remote address is valid,
|
||||
// and returns composed URL with needed username and password.
|
||||
func ParseRemoteAddr(remoteAddr, authUsername, authPassword string) (string, error) {
|
||||
remoteAddr = strings.TrimSpace(remoteAddr)
|
||||
// Remote address can be HTTP/HTTPS/Git URL or local path.
|
||||
if strings.HasPrefix(remoteAddr, "http://") ||
|
||||
strings.HasPrefix(remoteAddr, "https://") ||
|
||||
strings.HasPrefix(remoteAddr, "git://") {
|
||||
u, err := url.Parse(remoteAddr)
|
||||
if err != nil {
|
||||
return "", &ErrInvalidCloneAddr{IsURLError: true, Host: remoteAddr}
|
||||
}
|
||||
if len(authUsername)+len(authPassword) > 0 {
|
||||
u.User = url.UserPassword(authUsername, authPassword)
|
||||
}
|
||||
remoteAddr = u.String()
|
||||
}
|
||||
|
||||
return remoteAddr, nil
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/proxy"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// GPGSettings represents the default GPG settings for this repository
|
||||
@ -160,12 +159,6 @@ func CloneWithArgs(ctx context.Context, args TrustedCmdArgs, from, to string, op
|
||||
}
|
||||
cmd.AddDashesAndList(from, to)
|
||||
|
||||
if strings.Contains(from, "://") && strings.Contains(from, "@") {
|
||||
cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, util.SanitizeCredentialURLs(from), to, opts.Shared, opts.Mirror, opts.Depth))
|
||||
} else {
|
||||
cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, from, to, opts.Shared, opts.Mirror, opts.Depth))
|
||||
}
|
||||
|
||||
if opts.Timeout <= 0 {
|
||||
opts.Timeout = -1
|
||||
}
|
||||
@ -213,12 +206,6 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error {
|
||||
}
|
||||
cmd.AddDashesAndList(remoteBranchArgs...)
|
||||
|
||||
if strings.Contains(opts.Remote, "://") && strings.Contains(opts.Remote, "@") {
|
||||
cmd.SetDescription(fmt.Sprintf("push branch %s to %s (force: %t, mirror: %t)", opts.Branch, util.SanitizeCredentialURLs(opts.Remote), opts.Force, opts.Mirror))
|
||||
} else {
|
||||
cmd.SetDescription(fmt.Sprintf("push branch %s to %s (force: %t, mirror: %t)", opts.Branch, opts.Remote, opts.Force, opts.Mirror))
|
||||
}
|
||||
|
||||
stdout, stderr, err := cmd.RunStdString(&RunOpts{Env: opts.Env, Timeout: opts.Timeout, Dir: repoPath})
|
||||
if err != nil {
|
||||
if strings.Contains(stderr, "non-fast-forward") {
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/gtprof"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@ -136,7 +137,7 @@ func (g *Manager) doShutdown() {
|
||||
}
|
||||
g.lock.Lock()
|
||||
g.shutdownCtxCancel()
|
||||
atShutdownCtx := pprof.WithLabels(g.hammerCtx, pprof.Labels("graceful-lifecycle", "post-shutdown"))
|
||||
atShutdownCtx := pprof.WithLabels(g.hammerCtx, pprof.Labels(gtprof.LabelGracefulLifecycle, "post-shutdown"))
|
||||
pprof.SetGoroutineLabels(atShutdownCtx)
|
||||
for _, fn := range g.toRunAtShutdown {
|
||||
go fn()
|
||||
@ -167,7 +168,7 @@ func (g *Manager) doHammerTime(d time.Duration) {
|
||||
default:
|
||||
log.Warn("Setting Hammer condition")
|
||||
g.hammerCtxCancel()
|
||||
atHammerCtx := pprof.WithLabels(g.terminateCtx, pprof.Labels("graceful-lifecycle", "post-hammer"))
|
||||
atHammerCtx := pprof.WithLabels(g.terminateCtx, pprof.Labels(gtprof.LabelGracefulLifecycle, "post-hammer"))
|
||||
pprof.SetGoroutineLabels(atHammerCtx)
|
||||
}
|
||||
g.lock.Unlock()
|
||||
@ -183,7 +184,7 @@ func (g *Manager) doTerminate() {
|
||||
default:
|
||||
log.Warn("Terminating")
|
||||
g.terminateCtxCancel()
|
||||
atTerminateCtx := pprof.WithLabels(g.managerCtx, pprof.Labels("graceful-lifecycle", "post-terminate"))
|
||||
atTerminateCtx := pprof.WithLabels(g.managerCtx, pprof.Labels(gtprof.LabelGracefulLifecycle, "post-terminate"))
|
||||
pprof.SetGoroutineLabels(atTerminateCtx)
|
||||
|
||||
for _, fn := range g.toRunAtTerminate {
|
||||
|
@ -8,6 +8,8 @@ import (
|
||||
"runtime/pprof"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/gtprof"
|
||||
)
|
||||
|
||||
// FIXME: it seems that there is a bug when using systemd Type=notify: the "Install Page" (INSTALL_LOCK=false) doesn't notify properly.
|
||||
@ -65,10 +67,10 @@ func (g *Manager) prepare(ctx context.Context) {
|
||||
g.hammerCtx, g.hammerCtxCancel = context.WithCancel(ctx)
|
||||
g.managerCtx, g.managerCtxCancel = context.WithCancel(ctx)
|
||||
|
||||
g.terminateCtx = pprof.WithLabels(g.terminateCtx, pprof.Labels("graceful-lifecycle", "with-terminate"))
|
||||
g.shutdownCtx = pprof.WithLabels(g.shutdownCtx, pprof.Labels("graceful-lifecycle", "with-shutdown"))
|
||||
g.hammerCtx = pprof.WithLabels(g.hammerCtx, pprof.Labels("graceful-lifecycle", "with-hammer"))
|
||||
g.managerCtx = pprof.WithLabels(g.managerCtx, pprof.Labels("graceful-lifecycle", "with-manager"))
|
||||
g.terminateCtx = pprof.WithLabels(g.terminateCtx, pprof.Labels(gtprof.LabelGracefulLifecycle, "with-terminate"))
|
||||
g.shutdownCtx = pprof.WithLabels(g.shutdownCtx, pprof.Labels(gtprof.LabelGracefulLifecycle, "with-shutdown"))
|
||||
g.hammerCtx = pprof.WithLabels(g.hammerCtx, pprof.Labels(gtprof.LabelGracefulLifecycle, "with-hammer"))
|
||||
g.managerCtx = pprof.WithLabels(g.managerCtx, pprof.Labels(gtprof.LabelGracefulLifecycle, "with-manager"))
|
||||
|
||||
if !g.setStateTransition(stateInit, stateRunning) {
|
||||
panic("invalid graceful manager state: transition from init to running failed")
|
||||
|
25
modules/gtprof/gtprof.go
Normal file
25
modules/gtprof/gtprof.go
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package gtprof
|
||||
|
||||
// This is a Gitea-specific profiling package,
|
||||
// the name is chosen to distinguish it from the standard pprof tool and "GNU gprof"
|
||||
|
||||
// LabelGracefulLifecycle is a label marking manager lifecycle phase
|
||||
// Making it compliant with prometheus key regex https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels
|
||||
// would enable someone interested to be able to continuously gather profiles into pyroscope.
|
||||
// Other labels for pprof should also follow this rule.
|
||||
const LabelGracefulLifecycle = "graceful_lifecycle"
|
||||
|
||||
// LabelPid is a label set on goroutines that have a process attached
|
||||
const LabelPid = "pid"
|
||||
|
||||
// LabelPpid is a label set on goroutines that have a process attached
|
||||
const LabelPpid = "ppid"
|
||||
|
||||
// LabelProcessType is a label set on goroutines that have a process attached
|
||||
const LabelProcessType = "process_type"
|
||||
|
||||
// LabelProcessDescription is a label set on goroutines that have a process attached
|
||||
const LabelProcessDescription = "process_description"
|
@ -13,10 +13,9 @@ import (
|
||||
type Event struct {
|
||||
Time time.Time
|
||||
|
||||
GoroutinePid string
|
||||
Caller string
|
||||
Filename string
|
||||
Line int
|
||||
Caller string
|
||||
Filename string
|
||||
Line int
|
||||
|
||||
Level Level
|
||||
|
||||
@ -218,17 +217,16 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms
|
||||
}
|
||||
|
||||
if flags&Lgopid == Lgopid {
|
||||
if event.GoroutinePid != "" {
|
||||
buf = append(buf, '[')
|
||||
if mode.Colorize {
|
||||
buf = append(buf, ColorBytes(FgHiYellow)...)
|
||||
}
|
||||
buf = append(buf, event.GoroutinePid...)
|
||||
if mode.Colorize {
|
||||
buf = append(buf, resetBytes...)
|
||||
}
|
||||
buf = append(buf, ']', ' ')
|
||||
deprecatedGoroutinePid := "no-gopid" // use a dummy value to avoid breaking the log format
|
||||
buf = append(buf, '[')
|
||||
if mode.Colorize {
|
||||
buf = append(buf, ColorBytes(FgHiYellow)...)
|
||||
}
|
||||
buf = append(buf, deprecatedGoroutinePid...)
|
||||
if mode.Colorize {
|
||||
buf = append(buf, resetBytes...)
|
||||
}
|
||||
buf = append(buf, ']', ' ')
|
||||
}
|
||||
buf = append(buf, msg...)
|
||||
|
||||
|
@ -24,34 +24,32 @@ func TestItoa(t *testing.T) {
|
||||
func TestEventFormatTextMessage(t *testing.T) {
|
||||
res := EventFormatTextMessage(&WriterMode{Prefix: "[PREFIX] ", Colorize: false, Flags: Flags{defined: true, flags: 0xffffffff}},
|
||||
&Event{
|
||||
Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC),
|
||||
Caller: "caller",
|
||||
Filename: "filename",
|
||||
Line: 123,
|
||||
GoroutinePid: "pid",
|
||||
Level: ERROR,
|
||||
Stacktrace: "stacktrace",
|
||||
Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC),
|
||||
Caller: "caller",
|
||||
Filename: "filename",
|
||||
Line: 123,
|
||||
Level: ERROR,
|
||||
Stacktrace: "stacktrace",
|
||||
},
|
||||
"msg format: %v %v", "arg0", NewColoredValue("arg1", FgBlue),
|
||||
)
|
||||
|
||||
assert.Equal(t, `[PREFIX] 2020/01/02 03:04:05.000000 filename:123:caller [E] [pid] msg format: arg0 arg1
|
||||
assert.Equal(t, `[PREFIX] 2020/01/02 03:04:05.000000 filename:123:caller [E] [no-gopid] msg format: arg0 arg1
|
||||
stacktrace
|
||||
|
||||
`, string(res))
|
||||
|
||||
res = EventFormatTextMessage(&WriterMode{Prefix: "[PREFIX] ", Colorize: true, Flags: Flags{defined: true, flags: 0xffffffff}},
|
||||
&Event{
|
||||
Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC),
|
||||
Caller: "caller",
|
||||
Filename: "filename",
|
||||
Line: 123,
|
||||
GoroutinePid: "pid",
|
||||
Level: ERROR,
|
||||
Stacktrace: "stacktrace",
|
||||
Time: time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC),
|
||||
Caller: "caller",
|
||||
Filename: "filename",
|
||||
Line: 123,
|
||||
Level: ERROR,
|
||||
Stacktrace: "stacktrace",
|
||||
},
|
||||
"msg format: %v %v", "arg0", NewColoredValue("arg1", FgBlue),
|
||||
)
|
||||
|
||||
assert.Equal(t, "[PREFIX] \x1b[36m2020/01/02 03:04:05.000000 \x1b[0m\x1b[32mfilename:123:\x1b[32mcaller\x1b[0m \x1b[1;31m[E]\x1b[0m [\x1b[93mpid\x1b[0m] msg format: arg0 \x1b[34marg1\x1b[0m\n\tstacktrace\n\n", string(res))
|
||||
assert.Equal(t, "[PREFIX] \x1b[36m2020/01/02 03:04:05.000000 \x1b[0m\x1b[32mfilename:123:\x1b[32mcaller\x1b[0m \x1b[1;31m[E]\x1b[0m [\x1b[93mno-gopid\x1b[0m] msg format: arg0 \x1b[34marg1\x1b[0m\n\tstacktrace\n\n", string(res))
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ const (
|
||||
LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone
|
||||
Llevelinitial // Initial character of the provided level in brackets, eg. [I] for info
|
||||
Llevel // Provided level in brackets [INFO]
|
||||
Lgopid // the Goroutine-PID of the context
|
||||
Lgopid // the Goroutine-PID of the context, deprecated and it is always a const value
|
||||
|
||||
Lmedfile = Lshortfile | Llongfile // last 20 characters of the filename
|
||||
LstdFlags = Ldate | Ltime | Lmedfile | Lshortfuncname | Llevelinitial // default
|
||||
|
@ -1,19 +0,0 @@
|
||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package log
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:linkname runtime_getProfLabel runtime/pprof.runtime_getProfLabel
|
||||
func runtime_getProfLabel() unsafe.Pointer //nolint
|
||||
|
||||
type labelMap map[string]string
|
||||
|
||||
func getGoroutineLabels() map[string]string {
|
||||
l := (*labelMap)(runtime_getProfLabel())
|
||||
if l == nil {
|
||||
return nil
|
||||
}
|
||||
return *l
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime/pprof"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_getGoroutineLabels(t *testing.T) {
|
||||
pprof.Do(context.Background(), pprof.Labels(), func(ctx context.Context) {
|
||||
currentLabels := getGoroutineLabels()
|
||||
pprof.ForLabels(ctx, func(key, value string) bool {
|
||||
assert.EqualValues(t, value, currentLabels[key])
|
||||
return true
|
||||
})
|
||||
|
||||
pprof.Do(ctx, pprof.Labels("Test_getGoroutineLabels", "Test_getGoroutineLabels_child1"), func(ctx context.Context) {
|
||||
currentLabels := getGoroutineLabels()
|
||||
pprof.ForLabels(ctx, func(key, value string) bool {
|
||||
assert.EqualValues(t, value, currentLabels[key])
|
||||
return true
|
||||
})
|
||||
if assert.NotNil(t, currentLabels) {
|
||||
assert.EqualValues(t, "Test_getGoroutineLabels_child1", currentLabels["Test_getGoroutineLabels"])
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
@ -200,11 +200,6 @@ func (l *LoggerImpl) Log(skip int, level Level, format string, logArgs ...any) {
|
||||
event.Stacktrace = Stack(skip + 1)
|
||||
}
|
||||
|
||||
labels := getGoroutineLabels()
|
||||
if labels != nil {
|
||||
event.GoroutinePid = labels["pid"]
|
||||
}
|
||||
|
||||
// get a simple text message without color
|
||||
msgArgs := make([]any, len(logArgs))
|
||||
copy(msgArgs, logArgs)
|
||||
|
@ -24,7 +24,7 @@ type GlobalVarsType struct {
|
||||
LinkRegex *regexp.Regexp // fast matching a URL link, no any extra validation.
|
||||
}
|
||||
|
||||
var GlobalVars = sync.OnceValue[*GlobalVarsType](func() *GlobalVarsType {
|
||||
var GlobalVars = sync.OnceValue(func() *GlobalVarsType {
|
||||
v := &GlobalVarsType{}
|
||||
v.wwwURLRegxp = regexp.MustCompile(`^www\.[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}((?:/|[#?])[-a-zA-Z0-9@:%_\+.~#!?&//=\(\);,'">\^{}\[\]` + "`" + `]*)?`)
|
||||
v.LinkRegex, _ = xurls.StrictMatchingScheme("https?://")
|
||||
|
@ -42,7 +42,7 @@ type globalVarsType struct {
|
||||
nulCleaner *strings.Replacer
|
||||
}
|
||||
|
||||
var globalVars = sync.OnceValue[*globalVarsType](func() *globalVarsType {
|
||||
var globalVars = sync.OnceValue(func() *globalVarsType {
|
||||
v := &globalVarsType{}
|
||||
// NOTE: All below regex matching do not perform any extra validation.
|
||||
// Thus a link is produced even if the linked entity does not exist.
|
||||
|
@ -17,7 +17,7 @@ import (
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
var reAttrClass = sync.OnceValue[*regexp.Regexp](func() *regexp.Regexp {
|
||||
var reAttrClass = sync.OnceValue(func() *regexp.Regexp {
|
||||
// TODO: it isn't a problem at the moment because our HTML contents are always well constructed
|
||||
return regexp.MustCompile(`(<[^>]+)\s+class="([^"]+)"([^>]*>)`)
|
||||
})
|
||||
|
@ -112,7 +112,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
|
||||
}
|
||||
|
||||
// it is copied from old code, which is quite doubtful whether it is correct
|
||||
var reValidIconName = sync.OnceValue[*regexp.Regexp](func() *regexp.Regexp {
|
||||
var reValidIconName = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`^[-\w]+$`) // old: regexp.MustCompile("^[a-z ]+$")
|
||||
})
|
||||
|
||||
|
@ -129,7 +129,8 @@ func SpecializedMarkdown(ctx *markup.RenderContext) *GlodmarkRender {
|
||||
Enabled: setting.Markdown.EnableMath,
|
||||
ParseDollarInline: true,
|
||||
ParseDollarBlock: true,
|
||||
ParseSquareBlock: true, // TODO: this is a bad syntax, it should be deprecated in the future (by some config options)
|
||||
ParseSquareBlock: true, // TODO: this is a bad syntax "\[ ... \]", it conflicts with normal markdown escaping, it should be deprecated in the future (by some config options)
|
||||
// ParseBracketInline: true, // TODO: this is also a bad syntax "\( ... \)", it also conflicts, it should be deprecated in the future
|
||||
}),
|
||||
meta.Meta,
|
||||
),
|
||||
|
@ -43,8 +43,9 @@ var (
|
||||
ErrInvalidArchitecture = util.NewInvalidArgumentErrorf("package architecture is invalid")
|
||||
|
||||
// https://man.archlinux.org/man/PKGBUILD.5
|
||||
namePattern = regexp.MustCompile(`\A[a-zA-Z0-9@._+-]+\z`)
|
||||
versionPattern = regexp.MustCompile(`\A(?:[0-9]:)?[a-zA-Z0-9.+~]+(?:-[a-zA-Z0-9.+-~]+)?\z`)
|
||||
namePattern = regexp.MustCompile(`\A[a-zA-Z0-9@._+-]+\z`)
|
||||
// (epoch:pkgver-pkgrel)
|
||||
versionPattern = regexp.MustCompile(`\A(?:\d:)?[\w.+~]+(?:-[-\w.+~]+)?\z`)
|
||||
)
|
||||
|
||||
type Package struct {
|
||||
@ -69,10 +70,12 @@ type FileMetadata struct {
|
||||
Packager string `json:"packager,omitempty"`
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
Provides []string `json:"provides,omitempty"`
|
||||
Replaces []string `json:"replaces,omitempty"`
|
||||
Depends []string `json:"depends,omitempty"`
|
||||
OptDepends []string `json:"opt_depends,omitempty"`
|
||||
MakeDepends []string `json:"make_depends,omitempty"`
|
||||
CheckDepends []string `json:"check_depends,omitempty"`
|
||||
Conflicts []string `json:"conflicts,omitempty"`
|
||||
XData []string `json:"xdata,omitempty"`
|
||||
Backup []string `json:"backup,omitempty"`
|
||||
Files []string `json:"files,omitempty"`
|
||||
@ -201,12 +204,16 @@ func ParsePackageInfo(r io.Reader) (*Package, error) {
|
||||
p.FileMetadata.Provides = append(p.FileMetadata.Provides, value)
|
||||
case "depend":
|
||||
p.FileMetadata.Depends = append(p.FileMetadata.Depends, value)
|
||||
case "replaces":
|
||||
p.FileMetadata.Replaces = append(p.FileMetadata.Replaces, value)
|
||||
case "optdepend":
|
||||
p.FileMetadata.OptDepends = append(p.FileMetadata.OptDepends, value)
|
||||
case "makedepend":
|
||||
p.FileMetadata.MakeDepends = append(p.FileMetadata.MakeDepends, value)
|
||||
case "checkdepend":
|
||||
p.FileMetadata.CheckDepends = append(p.FileMetadata.CheckDepends, value)
|
||||
case "conflict":
|
||||
p.FileMetadata.Conflicts = append(p.FileMetadata.Conflicts, value)
|
||||
case "backup":
|
||||
p.FileMetadata.Backup = append(p.FileMetadata.Backup, value)
|
||||
case "group":
|
||||
|
@ -42,8 +42,10 @@ depend = gitea
|
||||
provides = common
|
||||
provides = gitea
|
||||
optdepend = hex
|
||||
replaces = gogs
|
||||
checkdepend = common
|
||||
makedepend = cmake
|
||||
conflict = ninja
|
||||
backup = usr/bin/paket1`)
|
||||
}
|
||||
|
||||
@ -120,6 +122,14 @@ func TestParsePackageInfo(t *testing.T) {
|
||||
assert.ErrorIs(t, err, ErrInvalidName)
|
||||
})
|
||||
|
||||
t.Run("Regexp", func(t *testing.T) {
|
||||
assert.Regexp(t, versionPattern, "1.2_3~4+5")
|
||||
assert.Regexp(t, versionPattern, "1:2_3~4+5")
|
||||
assert.NotRegexp(t, versionPattern, "a:1.0.0-1")
|
||||
assert.NotRegexp(t, versionPattern, "0.0.1/1-1")
|
||||
assert.NotRegexp(t, versionPattern, "1.0.0 -1")
|
||||
})
|
||||
|
||||
t.Run("InvalidVersion", func(t *testing.T) {
|
||||
data := createPKGINFOContent(packageName, "")
|
||||
|
||||
@ -149,8 +159,10 @@ func TestParsePackageInfo(t *testing.T) {
|
||||
assert.ElementsMatch(t, []string{"group"}, p.FileMetadata.Groups)
|
||||
assert.ElementsMatch(t, []string{"common", "gitea"}, p.FileMetadata.Provides)
|
||||
assert.ElementsMatch(t, []string{"common", "gitea"}, p.FileMetadata.Depends)
|
||||
assert.ElementsMatch(t, []string{"gogs"}, p.FileMetadata.Replaces)
|
||||
assert.ElementsMatch(t, []string{"hex"}, p.FileMetadata.OptDepends)
|
||||
assert.ElementsMatch(t, []string{"common"}, p.FileMetadata.CheckDepends)
|
||||
assert.ElementsMatch(t, []string{"ninja"}, p.FileMetadata.Conflicts)
|
||||
assert.ElementsMatch(t, []string{"cmake"}, p.FileMetadata.MakeDepends)
|
||||
assert.ElementsMatch(t, []string{"usr/bin/paket1"}, p.FileMetadata.Backup)
|
||||
})
|
||||
|
@ -32,7 +32,7 @@ func (c *Context) Value(key any) any {
|
||||
}
|
||||
|
||||
// ProcessContextKey is the key under which process contexts are stored
|
||||
var ProcessContextKey any = "process-context"
|
||||
var ProcessContextKey any = "process_context"
|
||||
|
||||
// GetContext will return a process context if one exists
|
||||
func GetContext(ctx context.Context) *Context {
|
||||
|
@ -11,6 +11,8 @@ import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/gtprof"
|
||||
)
|
||||
|
||||
// TODO: This packages still uses a singleton for the Manager.
|
||||
@ -25,18 +27,6 @@ var (
|
||||
DefaultContext = context.Background()
|
||||
)
|
||||
|
||||
// DescriptionPProfLabel is a label set on goroutines that have a process attached
|
||||
const DescriptionPProfLabel = "process-description"
|
||||
|
||||
// PIDPProfLabel is a label set on goroutines that have a process attached
|
||||
const PIDPProfLabel = "pid"
|
||||
|
||||
// PPIDPProfLabel is a label set on goroutines that have a process attached
|
||||
const PPIDPProfLabel = "ppid"
|
||||
|
||||
// ProcessTypePProfLabel is a label set on goroutines that have a process attached
|
||||
const ProcessTypePProfLabel = "process-type"
|
||||
|
||||
// IDType is a pid type
|
||||
type IDType string
|
||||
|
||||
@ -187,7 +177,12 @@ func (pm *Manager) Add(ctx context.Context, description string, cancel context.C
|
||||
|
||||
Trace(true, pid, description, parentPID, processType)
|
||||
|
||||
pprofCtx := pprof.WithLabels(ctx, pprof.Labels(DescriptionPProfLabel, description, PPIDPProfLabel, string(parentPID), PIDPProfLabel, string(pid), ProcessTypePProfLabel, processType))
|
||||
pprofCtx := pprof.WithLabels(ctx, pprof.Labels(
|
||||
gtprof.LabelProcessDescription, description,
|
||||
gtprof.LabelPpid, string(parentPID),
|
||||
gtprof.LabelPid, string(pid),
|
||||
gtprof.LabelProcessType, processType,
|
||||
))
|
||||
if currentlyRunning {
|
||||
pprof.SetGoroutineLabels(pprofCtx)
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/gtprof"
|
||||
|
||||
"github.com/google/pprof/profile"
|
||||
)
|
||||
|
||||
@ -202,7 +204,7 @@ func (pm *Manager) ProcessStacktraces(flat, noSystem bool) ([]*Process, int, int
|
||||
|
||||
// Add the non-process associated labels from the goroutine sample to the Stack
|
||||
for name, value := range sample.Label {
|
||||
if name == DescriptionPProfLabel || name == PIDPProfLabel || (!flat && name == PPIDPProfLabel) || name == ProcessTypePProfLabel {
|
||||
if name == gtprof.LabelProcessDescription || name == gtprof.LabelPid || (!flat && name == gtprof.LabelPpid) || name == gtprof.LabelProcessType {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -224,7 +226,7 @@ func (pm *Manager) ProcessStacktraces(flat, noSystem bool) ([]*Process, int, int
|
||||
var process *Process
|
||||
|
||||
// Try to get the PID from the goroutine labels
|
||||
if pidvalue, ok := sample.Label[PIDPProfLabel]; ok && len(pidvalue) == 1 {
|
||||
if pidvalue, ok := sample.Label[gtprof.LabelPid]; ok && len(pidvalue) == 1 {
|
||||
pid := IDType(pidvalue[0])
|
||||
|
||||
// Now try to get the process from our map
|
||||
@ -238,20 +240,20 @@ func (pm *Manager) ProcessStacktraces(flat, noSystem bool) ([]*Process, int, int
|
||||
|
||||
// get the parent PID
|
||||
ppid := IDType("")
|
||||
if value, ok := sample.Label[PPIDPProfLabel]; ok && len(value) == 1 {
|
||||
if value, ok := sample.Label[gtprof.LabelPpid]; ok && len(value) == 1 {
|
||||
ppid = IDType(value[0])
|
||||
}
|
||||
|
||||
// format the description
|
||||
description := "(dead process)"
|
||||
if value, ok := sample.Label[DescriptionPProfLabel]; ok && len(value) == 1 {
|
||||
if value, ok := sample.Label[gtprof.LabelProcessDescription]; ok && len(value) == 1 {
|
||||
description = value[0] + " " + description
|
||||
}
|
||||
|
||||
// override the type of the process to "code" but add the old type as a label on the first stack
|
||||
ptype := NoneProcessType
|
||||
if value, ok := sample.Label[ProcessTypePProfLabel]; ok && len(value) == 1 {
|
||||
stack.Labels = append(stack.Labels, &Label{Name: ProcessTypePProfLabel, Value: value[0]})
|
||||
if value, ok := sample.Label[gtprof.LabelProcessType]; ok && len(value) == 1 {
|
||||
stack.Labels = append(stack.Labels, &Label{Name: gtprof.LabelProcessType, Value: value[0]})
|
||||
}
|
||||
process = &Process{
|
||||
PID: pid,
|
||||
|
@ -6,6 +6,7 @@ package queue
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime/pprof"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@ -241,6 +242,9 @@ func NewWorkerPoolQueueWithContext[T any](ctx context.Context, name string, queu
|
||||
w.origHandler = handler
|
||||
w.safeHandler = func(t ...T) (unhandled []T) {
|
||||
defer func() {
|
||||
// FIXME: there is no ctx support in the handler, so process manager is unable to restore the labels
|
||||
// so here we explicitly set the "queue ctx" labels again after the handler is done
|
||||
pprof.SetGoroutineLabels(w.ctxRun)
|
||||
err := recover()
|
||||
if err != nil {
|
||||
log.Error("Recovered from panic in queue %q handler: %v\n%s", name, err, log.Stack(2))
|
||||
|
@ -32,7 +32,7 @@ var (
|
||||
// issueNumericPattern matches string that references to a numeric issue, e.g. #1287
|
||||
issueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|\'|\")([#!][0-9]+)(?:\s|$|\)|\]|\'|\"|[:;,.?!]\s|[:;,.?!]$)`)
|
||||
// issueAlphanumericPattern matches string that references to an alphanumeric issue, e.g. ABC-1234
|
||||
issueAlphanumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|\"|\')([A-Z]{1,10}-[1-9][0-9]*)(?:\s|$|\)|\]|:|\.(\s|$)|\"|\')`)
|
||||
issueAlphanumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[|\"|\')([A-Z]{1,10}-[1-9][0-9]*)(?:\s|$|\)|\]|:|\.(\s|$)|\"|\'|,)`)
|
||||
// crossReferenceIssueNumericPattern matches string that references a numeric issue in a different repository
|
||||
// e.g. org/repo#12345
|
||||
crossReferenceIssueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+[#!][0-9]+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`)
|
||||
|
@ -463,6 +463,7 @@ func TestRegExp_issueAlphanumericPattern(t *testing.T) {
|
||||
"ABC-123:",
|
||||
"\"ABC-123\"",
|
||||
"'ABC-123'",
|
||||
"ABC-123, unknown PR",
|
||||
}
|
||||
falseTestCases := []string{
|
||||
"RC-08",
|
||||
|
@ -12,6 +12,9 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
// CanUserForkBetweenOwners returns true if user can fork between owners.
|
||||
// By default, a user can fork a repository from another owner, but not from themselves.
|
||||
// Many users really like to fork their own repositories, so add an experimental setting to allow this.
|
||||
func CanUserForkBetweenOwners(id1, id2 int64) bool {
|
||||
if id1 != id2 {
|
||||
return true
|
||||
|
@ -31,12 +31,7 @@ func Test_getLicense(t *testing.T) {
|
||||
|
||||
Copyright (c) 2023 Gitea
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
`,
|
||||
Permission is hereby granted`,
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
@ -53,7 +48,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
||||
if !tt.wantErr(t, err, fmt.Sprintf("GetLicense(%v, %v)", tt.args.name, tt.args.values)) {
|
||||
return
|
||||
}
|
||||
assert.Equalf(t, tt.want, string(got), "GetLicense(%v, %v)", tt.args.name, tt.args.values)
|
||||
assert.Contains(t, string(got), tt.want, "GetLicense(%v, %v)", tt.args.name, tt.args.values)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ var defaultI18nLangNames = []string{
|
||||
"zh-TW", "繁體中文(台灣)",
|
||||
"de-DE", "Deutsch",
|
||||
"fr-FR", "Français",
|
||||
"ga-IE", "Gaeilge",
|
||||
"nl-NL", "Nederlands",
|
||||
"lv-LV", "Latviešu",
|
||||
"ru-RU", "Русский",
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
"html"
|
||||
"html/template"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -69,7 +68,7 @@ func NewFuncMap() template.FuncMap {
|
||||
// -----------------------------------------------------------------
|
||||
// time / number / format
|
||||
"FileSize": base.FileSize,
|
||||
"CountFmt": base.FormatNumberSI,
|
||||
"CountFmt": countFmt,
|
||||
"Sec2Time": util.SecToTime,
|
||||
|
||||
"TimeEstimateString": timeEstimateString,
|
||||
@ -239,29 +238,8 @@ func iif(condition any, vals ...any) any {
|
||||
}
|
||||
|
||||
func isTemplateTruthy(v any) bool {
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
rv := reflect.ValueOf(v)
|
||||
switch rv.Kind() {
|
||||
case reflect.Bool:
|
||||
return rv.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return rv.Int() != 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return rv.Uint() != 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return rv.Float() != 0
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return rv.Complex() != 0
|
||||
case reflect.String, reflect.Slice, reflect.Array, reflect.Map:
|
||||
return rv.Len() > 0
|
||||
case reflect.Struct:
|
||||
return true
|
||||
default:
|
||||
return !rv.IsNil()
|
||||
}
|
||||
truth, _ := template.IsTrue(v)
|
||||
return truth
|
||||
}
|
||||
|
||||
// evalTokens evaluates the expression by tokens and returns the result, see the comment of eval.Expr for details.
|
||||
@ -286,14 +264,6 @@ func userThemeName(user *user_model.User) string {
|
||||
return setting.UI.DefaultTheme
|
||||
}
|
||||
|
||||
func timeEstimateString(timeSec any) string {
|
||||
v, _ := util.ToInt64(timeSec)
|
||||
if v == 0 {
|
||||
return ""
|
||||
}
|
||||
return util.TimeEstimateString(v)
|
||||
}
|
||||
|
||||
// QueryBuild builds a query string from a list of key-value pairs.
|
||||
// It omits the nil and empty strings, but it doesn't omit other zero values,
|
||||
// because the zero value of number types may have a meaning.
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/htmlutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -65,31 +66,12 @@ func TestSanitizeHTML(t *testing.T) {
|
||||
assert.Equal(t, template.HTML(`<a href="/" rel="nofollow">link</a> xss <div>inline</div>`), SanitizeHTML(`<a href="/">link</a> <a href="javascript:">xss</a> <div style="dangerous">inline</div>`))
|
||||
}
|
||||
|
||||
func TestTemplateTruthy(t *testing.T) {
|
||||
func TestTemplateIif(t *testing.T) {
|
||||
tmpl := template.New("test")
|
||||
tmpl.Funcs(template.FuncMap{"Iif": iif})
|
||||
template.Must(tmpl.Parse(`{{if .Value}}true{{else}}false{{end}}:{{Iif .Value "true" "false"}}`))
|
||||
|
||||
cases := []any{
|
||||
nil, false, true, "", "string", 0, 1,
|
||||
byte(0), byte(1), int64(0), int64(1), float64(0), float64(1),
|
||||
complex(0, 0), complex(1, 0),
|
||||
(chan int)(nil), make(chan int),
|
||||
(func())(nil), func() {},
|
||||
util.ToPointer(0), util.ToPointer(util.ToPointer(0)),
|
||||
util.ToPointer(1), util.ToPointer(util.ToPointer(1)),
|
||||
[0]int{},
|
||||
[1]int{0},
|
||||
[]int(nil),
|
||||
[]int{},
|
||||
[]int{0},
|
||||
map[any]any(nil),
|
||||
map[any]any{},
|
||||
map[any]any{"k": "v"},
|
||||
(*struct{})(nil),
|
||||
struct{}{},
|
||||
util.ToPointer(struct{}{}),
|
||||
}
|
||||
cases := []any{nil, false, true, "", "string", 0, 1}
|
||||
w := &strings.Builder{}
|
||||
truthyCount := 0
|
||||
for i, v := range cases {
|
||||
@ -102,3 +84,37 @@ func TestTemplateTruthy(t *testing.T) {
|
||||
}
|
||||
assert.True(t, truthyCount != 0 && truthyCount != len(cases))
|
||||
}
|
||||
|
||||
func TestTemplateEscape(t *testing.T) {
|
||||
execTmpl := func(code string) string {
|
||||
tmpl := template.New("test")
|
||||
tmpl.Funcs(template.FuncMap{"QueryBuild": QueryBuild, "HTMLFormat": htmlutil.HTMLFormat})
|
||||
template.Must(tmpl.Parse(code))
|
||||
w := &strings.Builder{}
|
||||
assert.NoError(t, tmpl.Execute(w, nil))
|
||||
return w.String()
|
||||
}
|
||||
|
||||
t.Run("Golang URL Escape", func(t *testing.T) {
|
||||
// Golang template considers "href", "*src*", "*uri*", "*url*" (and more) ... attributes as contentTypeURL and does auto-escaping
|
||||
actual := execTmpl(`<a href="?a={{"%"}}"></a>`)
|
||||
assert.Equal(t, `<a href="?a=%25"></a>`, actual)
|
||||
actual = execTmpl(`<a data-xxx-url="?a={{"%"}}"></a>`)
|
||||
assert.Equal(t, `<a data-xxx-url="?a=%25"></a>`, actual)
|
||||
})
|
||||
t.Run("Golang URL No-escape", func(t *testing.T) {
|
||||
// non-URL content isn't auto-escaped
|
||||
actual := execTmpl(`<a data-link="?a={{"%"}}"></a>`)
|
||||
assert.Equal(t, `<a data-link="?a=%"></a>`, actual)
|
||||
})
|
||||
t.Run("QueryBuild", func(t *testing.T) {
|
||||
actual := execTmpl(`<a href="{{QueryBuild "?" "a" "%"}}"></a>`)
|
||||
assert.Equal(t, `<a href="?a=%25"></a>`, actual)
|
||||
actual = execTmpl(`<a href="?{{QueryBuild "a" "%"}}"></a>`)
|
||||
assert.Equal(t, `<a href="?a=%25"></a>`, actual)
|
||||
})
|
||||
t.Run("HTMLFormat", func(t *testing.T) {
|
||||
actual := execTmpl("{{HTMLFormat `<a k=\"%s\">%s</a>` `\"` `<>`}}")
|
||||
assert.Equal(t, `<a k="""><></a>`, actual)
|
||||
})
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ import (
|
||||
|
||||
type TemplateExecutor scopedtmpl.TemplateExecutor
|
||||
|
||||
type TplName string
|
||||
|
||||
type HTMLRender struct {
|
||||
templates atomic.Pointer[scopedtmpl.ScopedTemplate]
|
||||
}
|
||||
@ -40,7 +42,8 @@ var (
|
||||
|
||||
var ErrTemplateNotInitialized = errors.New("template system is not initialized, check your log for errors")
|
||||
|
||||
func (h *HTMLRender) HTML(w io.Writer, status int, name string, data any, ctx context.Context) error { //nolint:revive
|
||||
func (h *HTMLRender) HTML(w io.Writer, status int, tplName TplName, data any, ctx context.Context) error { //nolint:revive
|
||||
name := string(tplName)
|
||||
if respWriter, ok := w.(http.ResponseWriter); ok {
|
||||
if respWriter.Header().Get("Content-Type") == "" {
|
||||
respWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
|
37
modules/templates/util_format.go
Normal file
37
modules/templates/util_format.go
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
func timeEstimateString(timeSec any) string {
|
||||
v, _ := util.ToInt64(timeSec)
|
||||
if v == 0 {
|
||||
return ""
|
||||
}
|
||||
return util.TimeEstimateString(v)
|
||||
}
|
||||
|
||||
func countFmt(data any) string {
|
||||
// legacy code, not ideal, still used in some places
|
||||
num, err := util.ToInt64(data)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if num < 1000 {
|
||||
return fmt.Sprintf("%d", num)
|
||||
} else if num < 1_000_000 {
|
||||
num2 := float32(num) / 1000.0
|
||||
return fmt.Sprintf("%.1fk", num2)
|
||||
} else if num < 1_000_000_000 {
|
||||
num2 := float32(num) / 1_000_000.0
|
||||
return fmt.Sprintf("%.1fM", num2)
|
||||
}
|
||||
num2 := float32(num) / 1_000_000_000.0
|
||||
return fmt.Sprintf("%.1fG", num2)
|
||||
}
|
18
modules/templates/util_format_test.go
Normal file
18
modules/templates/util_format_test.go
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package templates
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCountFmt(t *testing.T) {
|
||||
assert.Equal(t, "125", countFmt(125))
|
||||
assert.Equal(t, "1.3k", countFmt(int64(1317)))
|
||||
assert.Equal(t, "21.3M", countFmt(21317675))
|
||||
assert.Equal(t, "45.7G", countFmt(45721317675))
|
||||
assert.Equal(t, "", countFmt("test"))
|
||||
}
|
@ -60,3 +60,7 @@ func (su *StringUtils) EllipsisString(s string, maxLength int) string {
|
||||
func (su *StringUtils) ToUpper(s string) string {
|
||||
return strings.ToUpper(s)
|
||||
}
|
||||
|
||||
func (su *StringUtils) TrimPrefix(s, prefix string) string {
|
||||
return strings.TrimPrefix(s, prefix)
|
||||
}
|
||||
|
@ -4,11 +4,16 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// RedirectURL returns the redirect URL of a http response.
|
||||
@ -41,3 +46,19 @@ func MockVariableValue[T any](p *T, v ...T) (reset func()) {
|
||||
}
|
||||
return func() { *p = old }
|
||||
}
|
||||
|
||||
// SetupGiteaRoot Sets GITEA_ROOT if it is not already set and returns the value
|
||||
func SetupGiteaRoot() string {
|
||||
giteaRoot := os.Getenv("GITEA_ROOT")
|
||||
if giteaRoot != "" {
|
||||
return giteaRoot
|
||||
}
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
giteaRoot = filepath.Dir(filepath.Dir(filepath.Dir(filename)))
|
||||
fixturesDir := filepath.Join(giteaRoot, "models", "fixtures")
|
||||
if exist, _ := util.IsDir(fixturesDir); !exist {
|
||||
panic(fmt.Sprintf("fixtures directory not found: %s", fixturesDir))
|
||||
}
|
||||
_ = os.Setenv("GITEA_ROOT", giteaRoot)
|
||||
return giteaRoot
|
||||
}
|
||||
|
17
modules/test/utils_test.go
Normal file
17
modules/test/utils_test.go
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSetupGiteaRoot(t *testing.T) {
|
||||
t.Setenv("GITEA_ROOT", "test")
|
||||
assert.Equal(t, "test", SetupGiteaRoot())
|
||||
t.Setenv("GITEA_ROOT", "")
|
||||
assert.NotEqual(t, "test", SetupGiteaRoot())
|
||||
}
|
13
modules/util/runtime.go
Normal file
13
modules/util/runtime.go
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package util
|
||||
|
||||
import "runtime"
|
||||
|
||||
func CallerFuncName(skip int) string {
|
||||
pc := make([]uintptr, 1)
|
||||
runtime.Callers(skip+1, pc)
|
||||
funcName := runtime.FuncForPC(pc[0]).Name()
|
||||
return funcName
|
||||
}
|
32
modules/util/runtime_test.go
Normal file
32
modules/util/runtime_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCallerFuncName(t *testing.T) {
|
||||
s := CallerFuncName(1)
|
||||
assert.Equal(t, "code.gitea.io/gitea/modules/util.TestCallerFuncName", s)
|
||||
}
|
||||
|
||||
func BenchmarkCallerFuncName(b *testing.B) {
|
||||
// BenchmarkCaller/sprintf-12 12744829 95.49 ns/op
|
||||
b.Run("sprintf", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = fmt.Sprintf("aaaaaaaaaaaaaaaa %s %s %s", "bbbbbbbbbbbbbbbbbbb", b.Name(), "ccccccccccccccccccccc")
|
||||
}
|
||||
})
|
||||
// BenchmarkCaller/caller-12 10625133 113.6 ns/op
|
||||
// It is almost as fast as fmt.Sprintf
|
||||
b.Run("caller", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
CallerFuncName(1)
|
||||
}
|
||||
})
|
||||
}
|
@ -25,7 +25,7 @@ type timeStrGlobalVarsType struct {
|
||||
// In the future, it could be some configurable options to help users
|
||||
// to convert the working time to different units.
|
||||
|
||||
var timeStrGlobalVars = sync.OnceValue[*timeStrGlobalVarsType](func() *timeStrGlobalVarsType {
|
||||
var timeStrGlobalVars = sync.OnceValue(func() *timeStrGlobalVarsType {
|
||||
v := &timeStrGlobalVarsType{}
|
||||
v.re = regexp.MustCompile(`(?i)(\d+)\s*([hms])`)
|
||||
v.units = []struct {
|
||||
|
@ -242,10 +242,10 @@ func TestReserveLineBreakForTextarea(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestOptionalArg(t *testing.T) {
|
||||
foo := func(other any, optArg ...int) int {
|
||||
foo := func(_ any, optArg ...int) int {
|
||||
return OptionalArg(optArg)
|
||||
}
|
||||
bar := func(other any, optArg ...int) int {
|
||||
bar := func(_ any, optArg ...int) int {
|
||||
return OptionalArg(optArg, 42)
|
||||
}
|
||||
assert.Equal(t, 0, foo(nil))
|
||||
|
31
options/gitignore/AutomationStudio
Normal file
31
options/gitignore/AutomationStudio
Normal file
@ -0,0 +1,31 @@
|
||||
# gitignore template for B&R Automation Studio (AS) 4
|
||||
# website: https://www.br-automation.com/en-us/products/software/automation-software/automation-studio/
|
||||
|
||||
# AS temporary directories
|
||||
Binaries/
|
||||
Diagnosis/
|
||||
Temp/
|
||||
TempObjects/
|
||||
|
||||
# AS transfer files
|
||||
*artransfer.br
|
||||
*arTrsfmode.nv
|
||||
|
||||
# 'ignored' directory
|
||||
ignored/
|
||||
|
||||
# ARNC0ext
|
||||
*arnc0ext.br
|
||||
|
||||
# AS File types
|
||||
*.bak
|
||||
*.isopen
|
||||
*.orig
|
||||
*.log
|
||||
*.asar
|
||||
*.csvlog*
|
||||
*.set
|
||||
!**/Physical/**/*.set
|
||||
|
||||
# RevInfo variables
|
||||
*RevInfo.var
|
28
options/gitignore/Firebase
Normal file
28
options/gitignore/Firebase
Normal file
@ -0,0 +1,28 @@
|
||||
# Firebase build and deployment files
|
||||
/firebase-debug.log
|
||||
/firebase-debug.*.log
|
||||
.firebaserc
|
||||
|
||||
# Firebase Hosting
|
||||
/firebase.json
|
||||
*.cache
|
||||
hosting/.cache
|
||||
|
||||
# Firebase Functions
|
||||
/functions/node_modules/
|
||||
/functions/.env
|
||||
/functions/package-lock.json
|
||||
|
||||
# Firebase Emulators
|
||||
/firebase-*.zip
|
||||
/.firebase/
|
||||
/emulator-ui/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Environment files (local configs)
|
||||
/.env.*
|
42
options/gitignore/Modelica
Normal file
42
options/gitignore/Modelica
Normal file
@ -0,0 +1,42 @@
|
||||
# Modelica - an object-oriented language for modeling of cyber-physical systems
|
||||
# https://modelica.org/
|
||||
# Ignore temporary files, build results, simulation files
|
||||
|
||||
## Modelica-specific files
|
||||
*~
|
||||
*.bak
|
||||
*.bak-mo
|
||||
*.mof
|
||||
\#*\#
|
||||
*.moe
|
||||
*.mol
|
||||
|
||||
## Build artefacts
|
||||
*.exe
|
||||
*.exp
|
||||
*.o
|
||||
*.pyc
|
||||
|
||||
## Simulation files
|
||||
*.mat
|
||||
|
||||
## Package files
|
||||
*.gz
|
||||
*.rar
|
||||
*.tar
|
||||
*.zip
|
||||
|
||||
## Dymola-specific files
|
||||
buildlog.txt
|
||||
dsfinal.txt
|
||||
dsin.txt
|
||||
dslog.txt
|
||||
dsmodel*
|
||||
dsres.txt
|
||||
dymosim*
|
||||
request
|
||||
stat
|
||||
status
|
||||
stop
|
||||
success
|
||||
*.
|
@ -166,3 +166,6 @@ cython_debug/
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
## Bibliography auxiliary files (bibtex/biblatex/biber):
|
||||
*.bbl
|
||||
*.bbl-SAVE-ERROR
|
||||
*.bcf
|
||||
*.blg
|
||||
*-blx.aux
|
||||
|
@ -2,18 +2,18 @@ Elastic License 2.0
|
||||
|
||||
URL: https://www.elastic.co/licensing/elastic-license
|
||||
|
||||
## Acceptance
|
||||
Acceptance
|
||||
|
||||
By using the software, you agree to all of the terms and conditions below.
|
||||
|
||||
## Copyright License
|
||||
Copyright License
|
||||
|
||||
The licensor grants you a non-exclusive, royalty-free, worldwide,
|
||||
non-sublicensable, non-transferable license to use, copy, distribute, make
|
||||
available, and prepare derivative works of the software, in each case subject to
|
||||
the limitations and conditions below.
|
||||
|
||||
## Limitations
|
||||
Limitations
|
||||
|
||||
You may not provide the software to third parties as a hosted or managed
|
||||
service, where the service provides users with access to any substantial set of
|
||||
@ -27,7 +27,7 @@ You may not alter, remove, or obscure any licensing, copyright, or other notices
|
||||
of the licensor in the software. Any use of the licensor’s trademarks is subject
|
||||
to applicable law.
|
||||
|
||||
## Patents
|
||||
Patents
|
||||
|
||||
The licensor grants you a license, under any patent claims the licensor can
|
||||
license, or becomes able to license, to make, have made, use, sell, offer for
|
||||
@ -40,7 +40,7 @@ the software granted under these terms ends immediately. If your company makes
|
||||
such a claim, your patent license ends immediately for work on behalf of your
|
||||
company.
|
||||
|
||||
## Notices
|
||||
Notices
|
||||
|
||||
You must ensure that anyone who gets a copy of any part of the software from you
|
||||
also gets a copy of these terms.
|
||||
@ -53,7 +53,7 @@ software prominent notices stating that you have modified the software.
|
||||
These terms do not imply any licenses other than those expressly granted in
|
||||
these terms.
|
||||
|
||||
## Termination
|
||||
Termination
|
||||
|
||||
If you use the software in violation of these terms, such use is not licensed,
|
||||
and your licenses will automatically terminate. If the licensor provides you
|
||||
@ -63,31 +63,31 @@ reinstated retroactively. However, if you violate these terms after such
|
||||
reinstatement, any additional violation of these terms will cause your licenses
|
||||
to terminate automatically and permanently.
|
||||
|
||||
## No Liability
|
||||
No Liability
|
||||
|
||||
*As far as the law allows, the software comes as is, without any warranty or
|
||||
As far as the law allows, the software comes as is, without any warranty or
|
||||
condition, and the licensor will not be liable to you for any damages arising
|
||||
out of these terms or the use or nature of the software, under any kind of
|
||||
legal claim.*
|
||||
legal claim.
|
||||
|
||||
## Definitions
|
||||
Definitions
|
||||
|
||||
The **licensor** is the entity offering these terms, and the **software** is the
|
||||
The licensor is the entity offering these terms, and the software is the
|
||||
software the licensor makes available under these terms, including any portion
|
||||
of it.
|
||||
|
||||
**you** refers to the individual or entity agreeing to these terms.
|
||||
you refers to the individual or entity agreeing to these terms.
|
||||
|
||||
**your company** is any legal entity, sole proprietorship, or other kind of
|
||||
your company is any legal entity, sole proprietorship, or other kind of
|
||||
organization that you work for, plus all organizations that have control over,
|
||||
are under the control of, or are under common control with that
|
||||
organization. **control** means ownership of substantially all the assets of an
|
||||
organization. control means ownership of substantially all the assets of an
|
||||
entity, or the power to direct its management and policies by vote, contract, or
|
||||
otherwise. Control can be direct or indirect.
|
||||
|
||||
**your licenses** are all the licenses granted to you for the software under
|
||||
your licenses are all the licenses granted to you for the software under
|
||||
these terms.
|
||||
|
||||
**use** means anything you do with the software requiring one of your licenses.
|
||||
use means anything you do with the software requiring one of your licenses.
|
||||
|
||||
**trademark** means trademarks, service marks, and similar rights.
|
||||
trademark means trademarks, service marks, and similar rights.
|
||||
|
4
options/license/MIPS
Normal file
4
options/license/MIPS
Normal file
@ -0,0 +1,4 @@
|
||||
Copyright (c) 1992, 1991, 1990 MIPS Computer Systems, Inc.
|
||||
MIPS Computer Systems, Inc. grants reproduction and use
|
||||
rights to all parties, PROVIDED that this comment is
|
||||
maintained in the copy.
|
@ -2,8 +2,17 @@ MIT License
|
||||
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
||||
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
7
options/license/ThirdEye
Normal file
7
options/license/ThirdEye
Normal file
@ -0,0 +1,7 @@
|
||||
(C) Copyright 1984 by Third Eye Software, Inc.
|
||||
|
||||
Third Eye Software, Inc. grants reproduction and use rights to
|
||||
all parties, PROVIDED that this comment is maintained in the copy.
|
||||
|
||||
Third Eye makes no claims about the applicability of this
|
||||
symbol table to a particular use.
|
@ -1946,8 +1946,8 @@ pulls.delete.title = Delete this pull request?
|
||||
pulls.delete.text = Do you really want to delete this pull request? (This will permanently remove all content. Consider closing it instead, if you intend to keep it archived)
|
||||
|
||||
pulls.recently_pushed_new_branches = You pushed on branch <strong>%[1]s</strong> %[2]s
|
||||
pulls.upstream_diverging_prompt_behind_1 = This branch is %d commit behind %s
|
||||
pulls.upstream_diverging_prompt_behind_n = This branch is %d commits behind %s
|
||||
pulls.upstream_diverging_prompt_behind_1 = This branch is %[1]d commit behind %[2]s
|
||||
pulls.upstream_diverging_prompt_behind_n = This branch is %[1]d commits behind %[2]s
|
||||
pulls.upstream_diverging_prompt_base_newer = The base branch %s has new changes
|
||||
pulls.upstream_diverging_merge = Sync fork
|
||||
|
||||
@ -3773,6 +3773,9 @@ variables.creation.success = The variable "%s" has been added.
|
||||
variables.update.failed = Failed to edit variable.
|
||||
variables.update.success = The variable has been edited.
|
||||
|
||||
logs.always_auto_scroll = Always auto scroll logs
|
||||
logs.always_expand_running = Always expand running logs
|
||||
|
||||
[projects]
|
||||
deleted.display_name = Deleted Project
|
||||
type-1.display_name = Individual Project
|
||||
|
@ -1109,6 +1109,7 @@ delete_preexisting_success=Fichiers dépossédés supprimés dans %s.
|
||||
blame_prior=Voir le blame avant cette modification
|
||||
blame.ignore_revs=Les révisions dans <a href="%s">.git-blame-ignore-revs</a> sont ignorées. Vous pouvez quand même <a href="%s">voir ces blâmes</a>.
|
||||
blame.ignore_revs.failed=Impossible d'ignorer les révisions dans <a href="%s">.git-blame-ignore-revs</a>.
|
||||
user_search_tooltip=Affiche un maximum de 30 utilisateurs
|
||||
|
||||
tree_path_not_found_commit=Le chemin %[1]s n’existe pas dans la révision %[2]s.
|
||||
tree_path_not_found_branch=Le chemin %[1]s n’existe pas dans la branche %[2]s.
|
||||
@ -1527,6 +1528,8 @@ issues.filter_assignee=Assigné
|
||||
issues.filter_assginee_no_select=Tous les assignés
|
||||
issues.filter_assginee_no_assignee=Aucun assigné
|
||||
issues.filter_poster=Auteur
|
||||
issues.filter_user_placeholder=Rechercher des utilisateurs
|
||||
issues.filter_user_no_select=Tous les utilisateurs
|
||||
issues.filter_type=Type
|
||||
issues.filter_type.all_issues=Tous les tickets
|
||||
issues.filter_type.assigned_to_you=Qui vous sont assignés
|
||||
@ -1676,7 +1679,6 @@ issues.timetracker_timer_stop=Arrêter le minuteur
|
||||
issues.timetracker_timer_discard=Annuler le minuteur
|
||||
issues.timetracker_timer_manually_add=Pointer du temps
|
||||
|
||||
issues.time_estimate_placeholder=1h 2m
|
||||
issues.time_estimate_set=Définir le temps estimé
|
||||
issues.time_estimate_display=Estimation : %s
|
||||
issues.change_time_estimate_at=a changé le temps estimé à <b>%s</b> %s
|
||||
@ -1943,8 +1945,6 @@ pulls.delete.title=Supprimer cette demande d'ajout ?
|
||||
pulls.delete.text=Voulez-vous vraiment supprimer cet demande d'ajout ? (Cela supprimera définitivement tout le contenu. Envisagez de le fermer à la place, si vous avez l'intention de le garder archivé)
|
||||
|
||||
pulls.recently_pushed_new_branches=Vous avez soumis sur la branche <strong>%[1]s</strong> %[2]s
|
||||
pulls.upstream_diverging_prompt_behind_1=Cette branche est en retard de %d révision sur %s
|
||||
pulls.upstream_diverging_prompt_behind_n=Cette branche est en retard de %d révisions sur %s
|
||||
pulls.upstream_diverging_prompt_base_newer=La branche de base %s a de nouveaux changements
|
||||
pulls.upstream_diverging_merge=Synchroniser la bifurcation
|
||||
|
||||
@ -2629,6 +2629,7 @@ release.new_release=Nouvelle publication
|
||||
release.draft=Brouillon
|
||||
release.prerelease=Pré-publication
|
||||
release.stable=Stable
|
||||
release.latest=Dernière
|
||||
release.compare=Comparer
|
||||
release.edit=Éditer
|
||||
release.ahead.commits=<strong>%d</strong> révisions
|
||||
|
@ -1109,6 +1109,7 @@ delete_preexisting_success=Scriosta comhaid neamhghlactha i %s
|
||||
blame_prior=Féach ar an milleán roimh an athrú seo
|
||||
blame.ignore_revs=Ag déanamh neamhairde de leasuithe i <a href="%s">.git-blame-ignore-revs</a>. Cliceáil <a href="%s">anseo chun seachaint</a> agus an gnáth-amharc milleán a fheiceáil.
|
||||
blame.ignore_revs.failed=Theip ar neamhaird a dhéanamh ar leasuithe i <a href="%s">.git-blame-ignore-revs</a>.
|
||||
user_search_tooltip=Taispeáint uasmhéid de 30 úsáideoir
|
||||
|
||||
tree_path_not_found_commit=Níl cosán %[1]s ann i dtiomantas %[2]s
|
||||
tree_path_not_found_branch=Níl cosán %[1]s ann i mbrainse %[2]s
|
||||
@ -1527,6 +1528,8 @@ issues.filter_assignee=Sannaitheoir
|
||||
issues.filter_assginee_no_select=Gach sannaithe
|
||||
issues.filter_assginee_no_assignee=Gan sannaitheoir
|
||||
issues.filter_poster=Údar
|
||||
issues.filter_user_placeholder=Cuardaigh úsáideoirí
|
||||
issues.filter_user_no_select=Gach úsáideoir
|
||||
issues.filter_type=Cineál
|
||||
issues.filter_type.all_issues=Gach saincheist
|
||||
issues.filter_type.assigned_to_you=Sannta duit
|
||||
@ -1676,7 +1679,6 @@ issues.timetracker_timer_stop=Stop an t-amadóir
|
||||
issues.timetracker_timer_discard=Déan an t-amadóir a scriosadh
|
||||
issues.timetracker_timer_manually_add=Cuir Am leis
|
||||
|
||||
issues.time_estimate_placeholder=1u 2n
|
||||
issues.time_estimate_set=Socraigh am measta
|
||||
issues.time_estimate_display=Meastachán: %s
|
||||
issues.change_time_estimate_at=d'athraigh an meastachán ama go <b>%s</b> %s
|
||||
@ -1943,8 +1945,6 @@ pulls.delete.title=Scrios an t-iarratas tarraingthe seo?
|
||||
pulls.delete.text=An bhfuil tú cinnte gur mhaith leat an t-iarratas tarraingthe seo a scriosadh? (Bainfidh sé seo an t-inneachar go léir go buan. Smaoinigh ar é a dhúnadh ina ionad sin, má tá sé i gceist agat é a choinneáil i gcartlann)
|
||||
|
||||
pulls.recently_pushed_new_branches=Bhrúigh tú ar bhrainse <strong>%[1]s</strong> %[2]s
|
||||
pulls.upstream_diverging_prompt_behind_1=Tá an brainse seo %d tiomantas taobh thiar de %s
|
||||
pulls.upstream_diverging_prompt_behind_n=Tá an brainse seo %d geallta taobh thiar de %s
|
||||
pulls.upstream_diverging_prompt_base_newer=Tá athruithe nua ar an mbunbhrainse %s
|
||||
pulls.upstream_diverging_merge=Forc sionc
|
||||
|
||||
@ -2629,6 +2629,7 @@ release.new_release=Scaoileadh Nua
|
||||
release.draft=Dréacht
|
||||
release.prerelease=Réamh-eisiúint
|
||||
release.stable=Cobhsaí
|
||||
release.latest=Is déanaí
|
||||
release.compare=Déan comparáid
|
||||
release.edit=cuir in eagar
|
||||
release.ahead.commits=Geallann <strong>%d</strong>
|
||||
|
@ -22,11 +22,25 @@ toc=Daftar Isi
|
||||
username=Nama Pengguna
|
||||
email=Alamat Email
|
||||
password=Kata Sandi
|
||||
re_type=Konfirmasi Kata Sandi
|
||||
captcha=CAPTCHA
|
||||
twofa=Otentikasi Dua Faktor
|
||||
twofa_scratch=Kode Awal Dua Faktor
|
||||
passcode=Kode Akses
|
||||
|
||||
webauthn_insert_key=Masukkan kunci keamanan anda
|
||||
webauthn_sign_in=Tekan tombol pada kunci keamanan Anda. Jika kunci keamanan Anda tidak memiliki tombol, masukkan kembali.
|
||||
webauthn_press_button=Silakan tekan tombol pada kunci keamanan Anda…
|
||||
webauthn_use_twofa=Gunakan kode dua faktor dari telepon Anda
|
||||
webauthn_error=Tidak dapat membaca kunci keamanan Anda.
|
||||
webauthn_unsupported_browser=Browser Anda saat ini tidak mendukung WebAuthn.
|
||||
webauthn_error_unknown=Terdapat kesalahan yang tidak diketahui. Mohon coba lagi.
|
||||
webauthn_error_insecure=`WebAuthn hanya mendukung koneksi aman. Untuk pengujian melalui HTTP, Anda dapat menggunakan "localhost" atau "127.0.0.1"`
|
||||
webauthn_error_unable_to_process=Server tidak dapat memproses permintaan Anda.
|
||||
webauthn_error_duplicated=Kunci keamanan tidak diperbolehkan untuk permintaan ini. Pastikan bahwa kunci ini belum terdaftar sebelumnya.
|
||||
webauthn_error_empty=Anda harus menetapkan nama untuk kunci ini.
|
||||
webauthn_error_timeout=Waktu habis sebelum kunci Anda dapat dibaca. Mohon muat ulang halaman ini dan coba lagi.
|
||||
webauthn_reload=Muat ulang
|
||||
|
||||
repository=Repositori
|
||||
organization=Organisasi
|
||||
@ -36,6 +50,8 @@ new_migrate=Migrasi Baru
|
||||
new_mirror=Duplikat Baru
|
||||
new_fork=Fork Repositori Baru
|
||||
new_org=Organisasi Baru
|
||||
new_project=Proyek Baru
|
||||
new_project_column=Kolom Baru
|
||||
manage_org=Mengelola Organisasi
|
||||
admin_panel=Administrasi Situs
|
||||
account_settings=Pengaturan Akun
|
||||
@ -55,29 +71,58 @@ pull_requests=Tarik Permintaan
|
||||
issues=Masalah
|
||||
milestones=Tonggak
|
||||
|
||||
ok=Oke
|
||||
cancel=Batal
|
||||
retry=Coba lagi
|
||||
rerun=Jalankan ulang
|
||||
rerun_all=Jalankan ulang semua job
|
||||
save=Simpan
|
||||
add=Tambah
|
||||
add_all=Tambah Semua
|
||||
remove=Buang
|
||||
remove_all=Buang Semua
|
||||
remove_label_str=`Hapus item "%s"`
|
||||
edit=Edit
|
||||
view=Tampilan
|
||||
test=Pengujian
|
||||
|
||||
enabled=Aktif
|
||||
disabled=Nonaktif
|
||||
locked=Terkunci
|
||||
|
||||
copy=Salin
|
||||
copy_url=Salin URL
|
||||
copy_hash=Salin hash
|
||||
copy_content=Salin konten
|
||||
copy_branch=Salin nama branch
|
||||
copy_success=Tersalin!
|
||||
copy_error=Gagal menyalin
|
||||
copy_type_unsupported=Tipe berkas ini tidak dapat disalin
|
||||
|
||||
write=Tulis
|
||||
preview=Pratinjau
|
||||
loading=Memuat…
|
||||
|
||||
error=Gangguan
|
||||
error404=Halaman yang akan kamu akses <strong>tidak dapat ditemukan</strong> atau <strong>kamu tidak memiliki akses </strong> untuk melihatnya.
|
||||
go_back=Kembali
|
||||
invalid_data=Data invalid: %v
|
||||
|
||||
never=Tidak Pernah
|
||||
unknown=Tidak diketahui
|
||||
|
||||
rss_feed=Umpan Berita
|
||||
|
||||
pin=Sematkan
|
||||
unpin=Lepas sematan
|
||||
|
||||
artifacts=Artefak
|
||||
confirm_delete_artifact=Apakah Anda yakin ingin menghapus artefak '%s' ?
|
||||
|
||||
archived=Diarsipkan
|
||||
|
||||
concept_system_global=Global
|
||||
concept_user_individual=Perorangan
|
||||
concept_code_repository=Repositori
|
||||
|
||||
show_full_screen=Tampilkan layar penuh
|
||||
@ -682,13 +727,16 @@ commits.newer=Terbaru
|
||||
commits.signed_by=Ditandai oleh
|
||||
|
||||
|
||||
commitstatus.error=Gangguan
|
||||
|
||||
|
||||
projects.description_placeholder=Deskripsi
|
||||
projects.title=Judul
|
||||
projects.new=Proyek Baru
|
||||
projects.template.desc=Contoh
|
||||
projects.column.edit_title=Nama
|
||||
projects.column.new_title=Nama
|
||||
projects.column.new=Kolom Baru
|
||||
|
||||
issues.new=Masalah Baru
|
||||
issues.new.labels=Label
|
||||
|
@ -145,6 +145,7 @@ confirm_delete_selected=選択したすべてのアイテムを削除してよ
|
||||
|
||||
name=名称
|
||||
value=値
|
||||
readme=Readme
|
||||
|
||||
filter=フィルター
|
||||
filter.clear=フィルターをクリア
|
||||
@ -1043,6 +1044,8 @@ generate_repo=リポジトリの生成
|
||||
generate_from=他からの生成
|
||||
repo_desc=説明
|
||||
repo_desc_helper=簡単な説明を入力してください (オプション)
|
||||
repo_no_desc=説明が提供されていません
|
||||
repo_lang=言語
|
||||
repo_gitignore_helper=.gitignoreテンプレートを選択してください。
|
||||
repo_gitignore_helper_desc=一般的な言語のテンプレートリストから、追跡しないファイルの設定を選択します。 各言語のビルドツールが生成する典型的なファイルが、デフォルトで.gitignoreに含まれます。
|
||||
issue_labels=イシューラベル
|
||||
@ -1666,12 +1669,25 @@ issues.delete.title=このイシューを削除しますか?
|
||||
issues.delete.text=本当にこのイシューを削除しますか? (これはすべてのコンテンツを完全に削除します。 保存しておきたい場合は、代わりにクローズすることを検討してください)
|
||||
|
||||
issues.tracker=タイムトラッカー
|
||||
issues.timetracker_timer_start=タイマー開始
|
||||
issues.timetracker_timer_stop=タイマー終了
|
||||
issues.timetracker_timer_discard=タイマー破棄
|
||||
issues.timetracker_timer_manually_add=時間を追加
|
||||
|
||||
issues.time_estimate_set=見積時間を設定
|
||||
issues.time_estimate_display=見積時間: %s
|
||||
issues.change_time_estimate_at=が見積時間を <b>%s</b> に変更 %s
|
||||
issues.remove_time_estimate_at=が見積時間を削除 %s
|
||||
issues.time_estimate_invalid=見積時間のフォーマットが不正です
|
||||
issues.start_tracking_history=が作業を開始 %s
|
||||
issues.tracker_auto_close=タイマーは、このイシューがクローズされると自動的に終了します
|
||||
issues.tracking_already_started=`<a href="%s">別のイシュー</a>で既にタイムトラッキングを開始しています!`
|
||||
issues.stop_tracking_history=が <b>%s</b> の作業を終了 %s
|
||||
issues.cancel_tracking_history=`がタイムトラッキングを中止 %s`
|
||||
issues.del_time=このタイムログを削除
|
||||
issues.add_time_history=が作業時間 <b>%s</b> を追加 %s
|
||||
issues.del_time_history=`が作業時間を削除 %s`
|
||||
issues.add_time_manually=時間の手入力
|
||||
issues.add_time_hours=時間
|
||||
issues.add_time_minutes=分
|
||||
issues.add_time_sum_to_small=時間が入力されていません。
|
||||
@ -1691,15 +1707,15 @@ issues.due_date_form_add=期日の追加
|
||||
issues.due_date_form_edit=変更
|
||||
issues.due_date_form_remove=削除
|
||||
issues.due_date_not_writer=イシューの期日を変更するには、リポジトリへの書き込み権限が必要です。
|
||||
issues.due_date_not_set=期日は未設定です。
|
||||
issues.due_date_not_set=期日は設定されていません。
|
||||
issues.due_date_added=が期日 %s を追加 %s
|
||||
issues.due_date_modified=が期日を %[2]s から %[1]s に変更 %[3]s
|
||||
issues.due_date_remove=が期日 %s を削除 %s
|
||||
issues.due_date_overdue=期日は過ぎています
|
||||
issues.due_date_invalid=期日が正しくないか範囲を超えています。 'yyyy-mm-dd' の形式で入力してください。
|
||||
issues.dependency.title=依存関係
|
||||
issues.dependency.issue_no_dependencies=依存関係が設定されていません。
|
||||
issues.dependency.pr_no_dependencies=依存関係が設定されていません。
|
||||
issues.dependency.issue_no_dependencies=依存関係は設定されていません。
|
||||
issues.dependency.pr_no_dependencies=依存関係は設定されていません。
|
||||
issues.dependency.no_permission_1=%d 個の依存関係への読み取り権限がありません
|
||||
issues.dependency.no_permission_n=%d 個の依存関係への読み取り権限がありません
|
||||
issues.dependency.no_permission.can_remove=この依存関係への読み取り権限はありませんが、この依存関係は削除できます
|
||||
@ -1924,6 +1940,8 @@ pulls.delete.title=このプルリクエストを削除しますか?
|
||||
pulls.delete.text=本当にこのプルリクエストを削除しますか? (これはすべてのコンテンツを完全に削除します。 保存しておきたい場合は、代わりにクローズすることを検討してください)
|
||||
|
||||
pulls.recently_pushed_new_branches=%[2]s 、あなたはブランチ <strong>%[1]s</strong> にプッシュしました
|
||||
pulls.upstream_diverging_prompt_base_newer=ベースブランチ %s に新しい変更があります
|
||||
pulls.upstream_diverging_merge=フォークを同期
|
||||
|
||||
pull.deleted_branch=(削除済み):%s
|
||||
pull.agit_documentation=AGitに関するドキュメントを確認する
|
||||
|
@ -1679,7 +1679,6 @@ issues.timetracker_timer_stop=Parar cronómetro
|
||||
issues.timetracker_timer_discard=Descartar cronómetro
|
||||
issues.timetracker_timer_manually_add=Adicionar tempo
|
||||
|
||||
issues.time_estimate_placeholder=1h 2m
|
||||
issues.time_estimate_set=Definir tempo estimado
|
||||
issues.time_estimate_display=Estimativa: %s
|
||||
issues.change_time_estimate_at=alterou a estimativa de tempo para <b>%s</b> %s
|
||||
@ -1946,8 +1945,6 @@ pulls.delete.title=Eliminar este pedido de integração?
|
||||
pulls.delete.text=Tem a certeza que quer eliminar este pedido de integração? Isso irá remover todo o conteúdo permanentemente. Como alternativa considere fechá-lo, se pretender mantê-lo em arquivo.
|
||||
|
||||
pulls.recently_pushed_new_branches=Enviou para o ramo <strong>%[1]s</strong> %[2]s
|
||||
pulls.upstream_diverging_prompt_behind_1=Este ramo está %d cometimento atrás de %s
|
||||
pulls.upstream_diverging_prompt_behind_n=Este ramo está %d cometimentos atrás de %s
|
||||
pulls.upstream_diverging_prompt_base_newer=O ramo base %s tem novas modificações
|
||||
pulls.upstream_diverging_merge=Sincronizar derivação
|
||||
|
||||
@ -2632,6 +2629,7 @@ release.new_release=Novo lançamento
|
||||
release.draft=Rascunho
|
||||
release.prerelease=Pré-lançamento
|
||||
release.stable=Estável
|
||||
release.latest=Mais recente
|
||||
release.compare=Comparar
|
||||
release.edit=editar
|
||||
release.ahead.commits=<strong>%d</strong> cometimentos
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user