diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index 0af524b143e..00000000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,1005 +0,0 @@ -const vitestPlugin = require('@vitest/eslint-plugin'); -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-wc', - ], - env: { - es2024: true, - node: true, - }, - overrides: [ - { - files: ['**/*.cjs'], - rules: { - 'import-x/no-commonjs': [0], - '@typescript-eslint/no-require-imports': [0], - }, - }, - { - 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'], - plugins: ['@vitest/eslint-plugin'], - globals: vitestPlugin.environments.env.globals, - rules: { - 'github/unescaped-html-literal': [0], - '@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': [2], - '@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/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-unnecessary-type-conversion': [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': [2, {functions: false, classes: true, variables: true, allowNamedExports: true, typedefs: false, enums: false, ignoreTypeReferences: true}], - '@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': [0], // https://github.com/github/eslint-plugin-github/issues/599 - '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': [2], - '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': [0], - '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': [0], // handled by @typescript-eslint/no-implied-eval - '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': [2], // it is still needed by eslint & IDE to prompt undefined names in real time - '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': [0], // handled by @typescript-eslint/no-use-before-define - '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-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'], - }, -}; diff --git a/.github/workflows/pull-compliance.yml b/.github/workflows/pull-compliance.yml index c6daee84115..6f8991ed4ee 100644 --- a/.github/workflows/pull-compliance.yml +++ b/.github/workflows/pull-compliance.yml @@ -35,11 +35,9 @@ jobs: - uses: astral-sh/setup-uv@v6 - run: uv python install 3.12 - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: 24 - cache: pnpm - cache-dependency-path: pnpm-lock.yaml - run: make deps-py - run: make deps-frontend - run: make lint-templates @@ -62,11 +60,9 @@ jobs: steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: 24 - cache: pnpm - cache-dependency-path: pnpm-lock.yaml - run: make deps-frontend - run: make lint-swagger @@ -134,11 +130,9 @@ jobs: steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: 24 - cache: pnpm - cache-dependency-path: pnpm-lock.yaml - run: make deps-frontend - run: make lint-frontend - run: make checks-frontend @@ -184,11 +178,9 @@ jobs: steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: 24 - cache: pnpm - cache-dependency-path: pnpm-lock.yaml - run: make deps-frontend - run: make lint-md diff --git a/.github/workflows/pull-e2e-tests.yml b/.github/workflows/pull-e2e-tests.yml index 9f42581e0c8..89b32260ca0 100644 --- a/.github/workflows/pull-e2e-tests.yml +++ b/.github/workflows/pull-e2e-tests.yml @@ -24,11 +24,9 @@ jobs: go-version-file: go.mod check-latest: true - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: 24 - cache: pnpm - cache-dependency-path: pnpm-lock.yaml - run: make deps-frontend frontend deps-backend - run: pnpm exec playwright install --with-deps - run: make test-e2e-sqlite diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml index 81041bf564c..3d652e4ad8b 100644 --- a/.github/workflows/release-nightly.yml +++ b/.github/workflows/release-nightly.yml @@ -21,11 +21,9 @@ jobs: go-version-file: go.mod check-latest: true - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: 24 - cache: pnpm - cache-dependency-path: pnpm-lock.yaml - run: make deps-frontend deps-backend # xgo build - run: make release diff --git a/.github/workflows/release-tag-rc.yml b/.github/workflows/release-tag-rc.yml index f7ca1d0a139..f4776a9ed8c 100644 --- a/.github/workflows/release-tag-rc.yml +++ b/.github/workflows/release-tag-rc.yml @@ -22,11 +22,9 @@ jobs: go-version-file: go.mod check-latest: true - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: 24 - cache: pnpm - cache-dependency-path: pnpm-lock.yaml - run: make deps-frontend deps-backend # xgo build - run: make release diff --git a/.github/workflows/release-tag-version.yml b/.github/workflows/release-tag-version.yml index 43ea6dce2fd..ad0820f31fe 100644 --- a/.github/workflows/release-tag-version.yml +++ b/.github/workflows/release-tag-version.yml @@ -26,11 +26,9 @@ jobs: go-version-file: go.mod check-latest: true - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v5 with: node-version: 24 - cache: pnpm - cache-dependency-path: pnpm-lock.yaml - run: make deps-frontend deps-backend # xgo build - run: make release diff --git a/Makefile b/Makefile index 1fb4a40d716..637e64210ac 100644 --- a/Makefile +++ b/Makefile @@ -162,7 +162,7 @@ TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(DIST) GO_DIRS := build cmd models modules routers services tests WEB_DIRS := web_src/js web_src/css -ESLINT_FILES := web_src/js tools *.ts *.cjs tests/e2e +ESLINT_FILES := web_src/js tools *.ts tests/e2e STYLELINT_FILES := web_src/css web_src/js/components/*.vue SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.md *.yml *.yaml *.toml)) $(filter-out tools/misspellings.csv, $(wildcard tools/*)) EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini @@ -346,12 +346,12 @@ lint-backend-fix: lint-go-fix lint-go-gitea-vet lint-editorconfig ## lint backen .PHONY: lint-js lint-js: node_modules ## lint js files - $(NODE_VARS) pnpm exec eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES) + $(NODE_VARS) pnpm exec eslint --color --max-warnings=0 --flag unstable_native_nodejs_ts_config $(ESLINT_FILES) $(NODE_VARS) pnpm exec vue-tsc .PHONY: lint-js-fix lint-js-fix: node_modules ## lint js files and fix issues - $(NODE_VARS) pnpm exec eslint --color --max-warnings=0 --ext js,ts,vue $(ESLINT_FILES) --fix + $(NODE_VARS) pnpm exec eslint --color --max-warnings=0 --flag unstable_native_nodejs_ts_config $(ESLINT_FILES) --fix $(NODE_VARS) pnpm exec vue-tsc .PHONY: lint-css diff --git a/assets/go-licenses.json b/assets/go-licenses.json index d645009d90f..9c19080e246 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -579,11 +579,6 @@ "path": "github.com/go-webauthn/x/revoke/LICENSE", "licenseText": "Copyright (c) 2014 CloudFlare Inc.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n\nRedistributions of source code must retain the above copyright notice,\nthis list of conditions and the following disclaimer.\n\nRedistributions in binary form must reproduce the above copyright notice,\nthis list of conditions and the following disclaimer in the documentation\nand/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" }, - { - "name": "github.com/gobwas/glob", - "path": "github.com/gobwas/glob/LICENSE", - "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2016 Sergey Kamardin\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE." - }, { "name": "github.com/goccy/go-json", "path": "github.com/goccy/go-json/LICENSE", diff --git a/cmd/dump.go b/cmd/dump.go index fa135079c09..7f0b23ed984 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -265,6 +265,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error { excludes = append(excludes, setting.LFS.Storage.Path) excludes = append(excludes, setting.Attachment.Storage.Path) excludes = append(excludes, setting.Packages.Storage.Path) + excludes = append(excludes, setting.RepoArchive.Storage.Path) excludes = append(excludes, setting.Log.RootPath) if err := dumper.AddRecursiveExclude("data", setting.AppDataPath, excludes); err != nil { fatal("Failed to include data directory: %v", err) diff --git a/cmd/embedded.go b/cmd/embedded.go index 1908352453f..9180407fd18 100644 --- a/cmd/embedded.go +++ b/cmd/embedded.go @@ -12,6 +12,7 @@ import ( "strings" "code.gitea.io/gitea/modules/assetfs" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/options" "code.gitea.io/gitea/modules/public" @@ -19,7 +20,6 @@ import ( "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/util" - "github.com/gobwas/glob" "github.com/urfave/cli/v3" ) diff --git a/cmd/hook.go b/cmd/hook.go index b741127ca3c..2f866dd396b 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -15,6 +15,7 @@ import ( "time" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" repo_module "code.gitea.io/gitea/modules/repository" @@ -312,7 +313,7 @@ func runHookPostReceive(ctx context.Context, c *cli.Command) error { setup(ctx, c.Bool("debug")) // First of all run update-server-info no matter what - if _, _, err := git.NewCommand("update-server-info").RunStdString(ctx, nil); err != nil { + if _, _, err := gitcmd.NewCommand("update-server-info").RunStdString(ctx, nil); err != nil { return fmt.Errorf("failed to call 'git update-server-info': %w", err) } diff --git a/cmd/serv.go b/cmd/serv.go index 089d0e3bb7c..76d8c81544e 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -21,6 +21,7 @@ import ( "code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/lfstransfer" "code.gitea.io/gitea/modules/log" @@ -229,11 +230,6 @@ func runServ(ctx context.Context, c *cli.Command) error { username := repoPathFields[0] reponame := strings.TrimSuffix(repoPathFields[1], ".git") // “the-repo-name" or "the-repo-name.wiki" - // LowerCase and trim the repoPath as that's how they are stored. - // This should be done after splitting the repoPath into username and reponame - // so that username and reponame are not affected. - repoPath = strings.ToLower(strings.TrimSpace(repoPath)) - if !repo.IsValidSSHAccessRepoName(reponame) { return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame) } @@ -280,6 +276,11 @@ func runServ(ctx context.Context, c *cli.Command) error { return fail(ctx, extra.UserMsg, "ServCommand failed: %s", extra.Error) } + // LowerCase and trim the repoPath as that's how they are stored. + // This should be done after splitting the repoPath into username and reponame + // so that username and reponame are not affected. + repoPath = strings.ToLower(results.OwnerName + "/" + results.RepoName + ".git") + // LFS SSH protocol if verb == git.CmdVerbLfsTransfer { token, err := getLFSAuthToken(ctx, lfsVerb, results) @@ -312,30 +313,30 @@ func runServ(ctx context.Context, c *cli.Command) error { return nil } - var gitcmd *exec.Cmd - gitBinPath := filepath.Dir(git.GitExecutable) // e.g. /usr/bin - gitBinVerb := filepath.Join(gitBinPath, verb) // e.g. /usr/bin/git-upload-pack + var command *exec.Cmd + gitBinPath := filepath.Dir(gitcmd.GitExecutable) // e.g. /usr/bin + gitBinVerb := filepath.Join(gitBinPath, verb) // e.g. /usr/bin/git-upload-pack if _, err := os.Stat(gitBinVerb); err != nil { // if the command "git-upload-pack" doesn't exist, try to split "git-upload-pack" to use the sub-command with git // ps: Windows only has "git.exe" in the bin path, so Windows always uses this way verbFields := strings.SplitN(verb, "-", 2) if len(verbFields) == 2 { // use git binary with the sub-command part: "C:\...\bin\git.exe", "upload-pack", ... - gitcmd = exec.CommandContext(ctx, git.GitExecutable, verbFields[1], repoPath) + command = exec.CommandContext(ctx, gitcmd.GitExecutable, verbFields[1], repoPath) } } - if gitcmd == nil { + if command == nil { // by default, use the verb (it has been checked above by allowedCommands) - gitcmd = exec.CommandContext(ctx, gitBinVerb, repoPath) + command = exec.CommandContext(ctx, gitBinVerb, repoPath) } - process.SetSysProcAttribute(gitcmd) - gitcmd.Dir = setting.RepoRootPath - gitcmd.Stdout = os.Stdout - gitcmd.Stdin = os.Stdin - gitcmd.Stderr = os.Stderr - gitcmd.Env = append(gitcmd.Env, os.Environ()...) - gitcmd.Env = append(gitcmd.Env, + process.SetSysProcAttribute(command) + command.Dir = setting.RepoRootPath + command.Stdout = os.Stdout + command.Stdin = os.Stdin + command.Stderr = os.Stderr + command.Env = append(command.Env, os.Environ()...) + command.Env = append(command.Env, repo_module.EnvRepoIsWiki+"="+strconv.FormatBool(results.IsWiki), repo_module.EnvRepoName+"="+results.RepoName, repo_module.EnvRepoUsername+"="+results.OwnerName, @@ -350,9 +351,9 @@ func runServ(ctx context.Context, c *cli.Command) error { ) // to avoid breaking, here only use the minimal environment variables for the "gitea serv" command. // it could be re-considered whether to use the same git.CommonGitCmdEnvs() as "git" command later. - gitcmd.Env = append(gitcmd.Env, git.CommonCmdServEnvs()...) + command.Env = append(command.Env, gitcmd.CommonCmdServEnvs()...) - if err = gitcmd.Run(); err != nil { + if err = command.Run(); err != nil { return fail(ctx, "Failed to execute git command", "Failed to execute git command: %v", err) } diff --git a/contrib/backport/README b/contrib/backport/README index 1e84c1bb974..466b79c6d49 100644 --- a/contrib/backport/README +++ b/contrib/backport/README @@ -11,7 +11,7 @@ The default version will read from `docs/config.yml`. You can override this using the option `--version`. The upstream branches will be fetched, using the remote `origin`. This can -be overrided using `--upstream`, and fetching can be avoided using +be overridden using `--upstream`, and fetching can be avoided using `--no-fetch`. By default the branch created will be called `backport-$PR-$VERSION`. You diff --git a/contrib/legal/privacy.html.sample b/contrib/legal/privacy.html.sample index 50972b2a3ec..adb3ea7ad4b 100644 --- a/contrib/legal/privacy.html.sample +++ b/contrib/legal/privacy.html.sample @@ -150,7 +150,7 @@

In general, Your Gitea Instance retains User Personal Information for as long as your account is active, or as needed to provide you service.

-

If you would like to cancel your account or delete your User Personal Information, you may do so in your user profile. We retain and use your information as necessary to comply with our legal obligations, resolve disputes, and enforce our agreements, but barring legal requirements, we will delete your full profile (within reason) within 90 days of your request. Feel free to contact our support to request erasure of the data we process on the bassis of consent within 30 days.

+

If you would like to cancel your account or delete your User Personal Information, you may do so in your user profile. We retain and use your information as necessary to comply with our legal obligations, resolve disputes, and enforce our agreements, but barring legal requirements, we will delete your full profile (within reason) within 90 days of your request. Feel free to contact our support to request erasure of the data we process on the basis of consent within 30 days.

After an account has been deleted, certain data, such as contributions to other Users' repositories and comments in others' issues, will remain. However, we will delete or de-identify your User Personal Information, including your username and email address, from the author field of issues, pull requests, and comments by associating them with a ghost user.

diff --git a/eslint.config.ts b/eslint.config.ts new file mode 100644 index 00000000000..678a49647c0 --- /dev/null +++ b/eslint.config.ts @@ -0,0 +1,1029 @@ +import arrayFunc from 'eslint-plugin-array-func'; +import comments from '@eslint-community/eslint-plugin-eslint-comments'; +import github from 'eslint-plugin-github'; +import globals from 'globals'; +import importPlugin from 'eslint-plugin-import-x'; +import noUseExtendNative from 'eslint-plugin-no-use-extend-native'; +import playwright from 'eslint-plugin-playwright'; +import regexp from 'eslint-plugin-regexp'; +import sonarjs from 'eslint-plugin-sonarjs'; +import stylistic from '@stylistic/eslint-plugin'; +import typescriptParser from '@typescript-eslint/parser'; +import typescriptPlugin from 'typescript-eslint'; +import unicorn from 'eslint-plugin-unicorn'; +import vitest from '@vitest/eslint-plugin'; +import vue from 'eslint-plugin-vue'; +import vueScopedCss from 'eslint-plugin-vue-scoped-css'; +import wc from 'eslint-plugin-wc'; +import {defineConfig, globalIgnores} from 'eslint/config'; + +const jsExts = ['js', 'mjs', 'cjs'] as const; +const tsExts = ['ts', 'mts', 'cts'] as const; +const restrictedSyntax = ['WithStatement', 'ForInStatement', 'LabeledStatement', 'SequenceExpression']; + +export default defineConfig([ + globalIgnores([ + 'web_src/js/vendor', + 'web_src/fomantic', + 'public/assets/js', + ]), + { + files: [`**/*.{${[...jsExts, ...tsExts].join(',')}}`], + ignores: ['dist/*'], + languageOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + globals: { + ...globals.browser, + ...globals.node, + }, + parser: typescriptParser, + parserOptions: { + sourceType: 'module', + ecmaVersion: 'latest', + ecmaFeatures: { + impliedStrict: true, + }, + project: true, + }, + }, + linterOptions: { + reportUnusedDisableDirectives: 2, + }, + plugins: { + '@eslint-community/eslint-comments': comments, + // @ts-expect-error + '@stylistic': stylistic, + '@typescript-eslint': typescriptPlugin.plugin, + 'array-func': arrayFunc, + // @ts-expect-error -- https://github.com/un-ts/eslint-plugin-import-x/issues/203 + 'import-x': importPlugin, + 'no-use-extend-native': noUseExtendNative, + // @ts-expect-error + regexp, + // @ts-expect-error + sonarjs, + // @ts-expect-error + unicorn, + github, + // @ts-expect-error + wc, + }, + settings: { + 'import-x/extensions': [...jsExts, ...tsExts].map((ext) => `.${ext}`), + 'import-x/parsers': {'@typescript-eslint/parser': [...jsExts, ...tsExts].map((ext) => `.${ext}`)}, + 'import-x/resolver': {'typescript': true}, + }, + 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/array-bracket-newline': [0], + '@stylistic/array-bracket-spacing': [2, 'never'], + '@stylistic/array-element-newline': [0], + '@stylistic/arrow-parens': [2, 'always'], + '@stylistic/arrow-spacing': [2, {before: true, after: true}], + '@stylistic/block-spacing': [0], + '@stylistic/brace-style': [2, '1tbs', {allowSingleLine: true}], + '@stylistic/comma-dangle': [2, 'always-multiline'], + '@stylistic/comma-spacing': [2, {before: false, after: true}], + '@stylistic/comma-style': [2, 'last'], + '@stylistic/computed-property-spacing': [2, 'never'], + '@stylistic/dot-location': [2, 'property'], + '@stylistic/eol-last': [2], + '@stylistic/function-call-argument-newline': [0], + '@stylistic/function-call-spacing': [2, 'never'], + '@stylistic/function-paren-newline': [0], + '@stylistic/generator-star-spacing': [0], + '@stylistic/implicit-arrow-linebreak': [0], + '@stylistic/indent': [2, 2, {ignoreComments: true, SwitchCase: 1}], + '@stylistic/key-spacing': [2], + '@stylistic/keyword-spacing': [2], + '@stylistic/line-comment-position': [0], + '@stylistic/linebreak-style': [2, 'unix'], + '@stylistic/lines-around-comment': [0], + '@stylistic/lines-between-class-members': [0], + '@stylistic/max-len': [0], + '@stylistic/max-statements-per-line': [0], + '@stylistic/multiline-comment-style': [0], + '@stylistic/multiline-ternary': [0], + '@stylistic/new-parens': [2], + '@stylistic/newline-per-chained-call': [0], + '@stylistic/no-confusing-arrow': [0], + '@stylistic/no-extra-parens': [0], + '@stylistic/no-extra-semi': [2], + '@stylistic/no-floating-decimal': [0], + '@stylistic/no-mixed-operators': [0], + '@stylistic/no-mixed-spaces-and-tabs': [2], + '@stylistic/no-multi-spaces': [2, {ignoreEOLComments: true, exceptions: {Property: true}}], + '@stylistic/no-multiple-empty-lines': [2, {max: 1, maxEOF: 0, maxBOF: 0}], + '@stylistic/no-tabs': [2], + '@stylistic/no-trailing-spaces': [2], + '@stylistic/no-whitespace-before-property': [2], + '@stylistic/nonblock-statement-body-position': [2], + '@stylistic/object-curly-newline': [0], + '@stylistic/object-curly-spacing': [2, 'never'], + '@stylistic/object-property-newline': [0], + '@stylistic/one-var-declaration-per-line': [0], + '@stylistic/operator-linebreak': [2, 'after'], + '@stylistic/padded-blocks': [2, 'never'], + '@stylistic/padding-line-between-statements': [0], + '@stylistic/quote-props': [0], + '@stylistic/quotes': [2, 'single', {avoidEscape: true, allowTemplateLiterals: 'always'}], + '@stylistic/rest-spread-spacing': [2, 'never'], + '@stylistic/semi': [2, 'always', {omitLastInOneLineBlock: true}], + '@stylistic/semi-spacing': [2, {before: false, after: true}], + '@stylistic/semi-style': [2, 'last'], + '@stylistic/space-before-blocks': [2, 'always'], + '@stylistic/space-before-function-paren': [2, {anonymous: 'ignore', named: 'never', asyncArrow: 'always'}], + '@stylistic/space-in-parens': [2, 'never'], + '@stylistic/space-infix-ops': [2], + '@stylistic/space-unary-ops': [2], + '@stylistic/spaced-comment': [2, 'always'], + '@stylistic/switch-colon-spacing': [2], + '@stylistic/template-curly-spacing': [2, 'never'], + '@stylistic/template-tag-spacing': [2, 'never'], + '@stylistic/wrap-iife': [2, 'inside'], + '@stylistic/wrap-regex': [0], + '@stylistic/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-unnecessary-type-conversion': [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': [2, {functions: false, classes: true, variables: true, allowNamedExports: true, typedefs: false, enums: false, ignoreTypeReferences: true}], + '@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': [0], // https://github.com/github/eslint-plugin-github/issues/599 + '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': [2], + '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}], // not compatible with eslint 9 + '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': [0], + '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': [0], // handled by @typescript-eslint/no-implied-eval + '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': [2], // it is still needed by eslint & IDE to prompt undefined names in real time + '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': [0], // handled by @typescript-eslint/no-use-before-define + '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-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-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'], + }, + }, + // @ts-expect-error + { + ...playwright.configs['flat/recommended'], + files: ['tests/e2e/**'], + rules: { + ...playwright.configs['flat/recommended'].rules, + }, + }, + { + files: ['**/*.vue'], + languageOptions: { + parserOptions: { + parser: '@typescript-eslint/parser', + }, + }, + extends: [ + // @ts-expect-error + vue.configs['flat/recommended'], + // @ts-expect-error + vueScopedCss.configs['flat/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: ['web_src/js/modules/fetch.ts', 'web_src/js/standalone/**/*'], + rules: { + 'no-restricted-syntax': [2, ...restrictedSyntax], + }, + }, + { + files: ['**/*.test.ts', 'web_src/js/test/setup.ts'], + // @ts-expect-error - https://github.com/vitest-dev/eslint-plugin-vitest/issues/737 + plugins: {vitest}, + languageOptions: {globals: globals.vitest}, + rules: { + 'github/unescaped-html-literal': [0], + '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': [2], + '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/types.ts'], + 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: ['*.config.*'], + rules: { + 'import-x/no-unused-modules': [0], + }, + }, + { + files: ['web_src/**/*', 'docs/**/*'], + languageOptions: {globals: globals.browser}, + }, + { + files: ['web_src/**/*'], + languageOptions: { + globals: { + ...globals.browser, + __webpack_public_path__: true, + process: false, // https://github.com/webpack/webpack/issues/15833 + }, + }, + }, +]); diff --git a/go.mod b/go.mod index eb871e24ae1..f32c3e08ef4 100644 --- a/go.mod +++ b/go.mod @@ -61,7 +61,6 @@ require ( github.com/go-redsync/redsync/v4 v4.13.0 github.com/go-sql-driver/mysql v1.9.3 github.com/go-webauthn/webauthn v0.13.4 - github.com/gobwas/glob v0.2.3 github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 github.com/golang-jwt/jwt/v5 v5.3.0 diff --git a/go.sum b/go.sum index b69b380cfe2..1853693e90d 100644 --- a/go.sum +++ b/go.sum @@ -364,8 +364,6 @@ github.com/go-webauthn/webauthn v0.13.4 h1:q68qusWPcqHbg9STSxBLBHnsKaLxNO0RnVKaA github.com/go-webauthn/webauthn v0.13.4/go.mod h1:MglN6OH9ECxvhDqoq1wMoF6P6JRYDiQpC9nc5OomQmI= github.com/go-webauthn/x v0.1.24 h1:6LaWf2zzWqbyKT8IyQkhje1/1KCGhlEkMz4V1tDnt/A= github.com/go-webauthn/x v0.1.24/go.mod h1:2o5XKJ+X1AKqYKGgHdKflGnoQFQZ6flJ2IFCBKSbSOw= -github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= diff --git a/models/asymkey/gpg_key_add.go b/models/asymkey/gpg_key_add.go index 1c7d2c1da26..3969edcc869 100644 --- a/models/asymkey/gpg_key_add.go +++ b/models/asymkey/gpg_key_add.go @@ -72,96 +72,90 @@ func AddGPGKey(ctx context.Context, ownerID int64, content, token, signature str return nil, err } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, err - } - defer committer.Close() + return db.WithTx2(ctx, func(ctx context.Context) ([]*GPGKey, error) { + keys := make([]*GPGKey, 0, len(ekeys)) - keys := make([]*GPGKey, 0, len(ekeys)) - - verified := false - // Handle provided signature - if signature != "" { - signer, err := openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token), strings.NewReader(signature), nil) - if err != nil { - signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\n"), strings.NewReader(signature), nil) - } - if err != nil { - signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\r\n"), strings.NewReader(signature), nil) - } - if err != nil { - log.Debug("AddGPGKey CheckArmoredDetachedSignature failed: %v", err) - return nil, ErrGPGInvalidTokenSignature{ - ID: ekeys[0].PrimaryKey.KeyIdString(), - Wrapped: err, + verified := false + // Handle provided signature + if signature != "" { + signer, err := openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token), strings.NewReader(signature), nil) + if err != nil { + signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\n"), strings.NewReader(signature), nil) } + if err != nil { + signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\r\n"), strings.NewReader(signature), nil) + } + if err != nil { + log.Debug("AddGPGKey CheckArmoredDetachedSignature failed: %v", err) + return nil, ErrGPGInvalidTokenSignature{ + ID: ekeys[0].PrimaryKey.KeyIdString(), + Wrapped: err, + } + } + ekeys = []*openpgp.Entity{signer} + verified = true } - ekeys = []*openpgp.Entity{signer} - verified = true - } - if len(ekeys) > 1 { - id2key := map[string]*openpgp.Entity{} - newEKeys := make([]*openpgp.Entity, 0, len(ekeys)) - for _, ekey := range ekeys { - id := ekey.PrimaryKey.KeyIdString() - if original, has := id2key[id]; has { - // Coalesce this with the other one - for _, subkey := range ekey.Subkeys { - if subkey.PublicKey == nil { - continue - } - found := false - - for _, originalSubkey := range original.Subkeys { - if originalSubkey.PublicKey == nil { + if len(ekeys) > 1 { + id2key := map[string]*openpgp.Entity{} + newEKeys := make([]*openpgp.Entity, 0, len(ekeys)) + for _, ekey := range ekeys { + id := ekey.PrimaryKey.KeyIdString() + if original, has := id2key[id]; has { + // Coalesce this with the other one + for _, subkey := range ekey.Subkeys { + if subkey.PublicKey == nil { continue } - if originalSubkey.PublicKey.KeyId == subkey.PublicKey.KeyId { - found = true - break + found := false + + for _, originalSubkey := range original.Subkeys { + if originalSubkey.PublicKey == nil { + continue + } + if originalSubkey.PublicKey.KeyId == subkey.PublicKey.KeyId { + found = true + break + } + } + if !found { + original.Subkeys = append(original.Subkeys, subkey) } } - if !found { - original.Subkeys = append(original.Subkeys, subkey) + for name, identity := range ekey.Identities { + if _, has := original.Identities[name]; has { + continue + } + original.Identities[name] = identity } + continue } - for name, identity := range ekey.Identities { - if _, has := original.Identities[name]; has { - continue - } - original.Identities[name] = identity - } - continue + id2key[id] = ekey + newEKeys = append(newEKeys, ekey) } - id2key[id] = ekey - newEKeys = append(newEKeys, ekey) - } - ekeys = newEKeys - } - - for _, ekey := range ekeys { - // Key ID cannot be duplicated. - has, err := db.GetEngine(ctx).Where("key_id=?", ekey.PrimaryKey.KeyIdString()). - Get(new(GPGKey)) - if err != nil { - return nil, err - } else if has { - return nil, ErrGPGKeyIDAlreadyUsed{ekey.PrimaryKey.KeyIdString()} + ekeys = newEKeys } - // Get DB session + for _, ekey := range ekeys { + // Key ID cannot be duplicated. + has, err := db.GetEngine(ctx).Where("key_id=?", ekey.PrimaryKey.KeyIdString()). + Get(new(GPGKey)) + if err != nil { + return nil, err + } else if has { + return nil, ErrGPGKeyIDAlreadyUsed{ekey.PrimaryKey.KeyIdString()} + } - key, err := parseGPGKey(ctx, ownerID, ekey, verified) - if err != nil { - return nil, err - } + key, err := parseGPGKey(ctx, ownerID, ekey, verified) + if err != nil { + return nil, err + } - if err = addGPGKey(ctx, key, content); err != nil { - return nil, err + if err = addGPGKey(ctx, key, content); err != nil { + return nil, err + } + keys = append(keys, key) } - keys = append(keys, key) - } - return keys, committer.Commit() + return keys, nil + }) } diff --git a/models/asymkey/gpg_key_commit_verification.go b/models/asymkey/gpg_key_commit_verification.go index b85374e0735..375b703f7b3 100644 --- a/models/asymkey/gpg_key_commit_verification.go +++ b/models/asymkey/gpg_key_commit_verification.go @@ -25,7 +25,7 @@ type CommitVerification struct { SigningUser *user_model.User // if Verified, then SigningUser is non-nil CommittingUser *user_model.User // if Verified, then CommittingUser is non-nil SigningEmail string - SigningKey *GPGKey + SigningKey *GPGKey // FIXME: need to refactor it to a new name like "SigningGPGKey", it is also used in some templates SigningSSHKey *PublicKey TrustStatus string } diff --git a/models/asymkey/key_display.go b/models/asymkey/key_display.go new file mode 100644 index 00000000000..ee17553b5b4 --- /dev/null +++ b/models/asymkey/key_display.go @@ -0,0 +1,37 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package asymkey + +import ( + "os" + + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" +) + +func GetDisplaySigningKey(key *git.SigningKey) string { + if key == nil || key.Format == "" { + return "" + } + + switch key.Format { + case git.SigningKeyFormatOpenPGP: + return key.KeyID + case git.SigningKeyFormatSSH: + content, err := os.ReadFile(key.KeyID) + if err != nil { + log.Error("Unable to read SSH key %s: %v", key.KeyID, err) + return "(Unable to read SSH key)" + } + display, err := CalcFingerprint(string(content)) + if err != nil { + log.Error("Unable to calculate fingerprint for SSH key %s: %v", key.KeyID, err) + return "(Unable to calculate fingerprint for SSH key)" + } + return display + } + setting.PanicInDevOrTesting("Unknown signing key format: %s", key.Format) + return "(Unknown key format)" +} diff --git a/models/db/log.go b/models/db/log.go index a9df6f541db..4f563a4df04 100644 --- a/models/db/log.go +++ b/models/db/log.go @@ -67,7 +67,7 @@ func (l *XORMLogBridge) Warn(v ...any) { l.Log(stackLevel, log.WARN, "%s", fmt.Sprint(v...)) } -// Warnf show warnning log +// Warnf show warning log func (l *XORMLogBridge) Warnf(format string, v ...any) { l.Log(stackLevel, log.WARN, format, v...) } diff --git a/models/fixtures/user_redirect.yml b/models/fixtures/user_redirect.yml index 8ff79933983..c668cb6c3b7 100644 --- a/models/fixtures/user_redirect.yml +++ b/models/fixtures/user_redirect.yml @@ -2,3 +2,7 @@ id: 1 lower_name: olduser1 redirect_user_id: 1 +- + id: 2 + lower_name: olduser2 + redirect_user_id: 2 diff --git a/models/git/branch.go b/models/git/branch.go index 6021e1101f6..54351649cc5 100644 --- a/models/git/branch.go +++ b/models/git/branch.go @@ -334,122 +334,111 @@ func FindRenamedBranch(ctx context.Context, repoID int64, from string) (branch * // RenameBranch rename a branch func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to string, gitAction func(ctx context.Context, isDefault bool) error) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() + return db.WithTx(ctx, func(ctx context.Context) error { + sess := db.GetEngine(ctx) - sess := db.GetEngine(ctx) - - // check whether from branch exist - var branch Branch - exist, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, from).Get(&branch) - if err != nil { - return err - } else if !exist || branch.IsDeleted { - return ErrBranchNotExist{ - RepoID: repo.ID, - BranchName: from, - } - } - - // check whether to branch exist or is_deleted - var dstBranch Branch - exist, err = db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, to).Get(&dstBranch) - if err != nil { - return err - } - if exist { - if !dstBranch.IsDeleted { - return ErrBranchAlreadyExists{ - BranchName: to, + // check whether from branch exist + var branch Branch + exist, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, from).Get(&branch) + if err != nil { + return err + } else if !exist || branch.IsDeleted { + return ErrBranchNotExist{ + RepoID: repo.ID, + BranchName: from, } } - if _, err := db.GetEngine(ctx).ID(dstBranch.ID).NoAutoCondition().Delete(&dstBranch); err != nil { - return err - } - } - - // 1. update branch in database - if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Update(&Branch{ - Name: to, - }); err != nil { - return err - } else if n <= 0 { - return ErrBranchNotExist{ - RepoID: repo.ID, - BranchName: from, - } - } - - // 2. update default branch if needed - isDefault := repo.DefaultBranch == from - if isDefault { - repo.DefaultBranch = to - _, err = sess.ID(repo.ID).Cols("default_branch").Update(repo) + // check whether to branch exist or is_deleted + var dstBranch Branch + exist, err = db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, to).Get(&dstBranch) if err != nil { return err } - } + if exist { + if !dstBranch.IsDeleted { + return ErrBranchAlreadyExists{ + BranchName: to, + } + } - // 3. Update protected branch if needed - protectedBranch, err := GetProtectedBranchRuleByName(ctx, repo.ID, from) - if err != nil { - return err - } + if _, err := db.GetEngine(ctx).ID(dstBranch.ID).NoAutoCondition().Delete(&dstBranch); err != nil { + return err + } + } - if protectedBranch != nil { - // there is a protect rule for this branch - protectedBranch.RuleName = to - _, err = sess.ID(protectedBranch.ID).Cols("branch_name").Update(protectedBranch) + // 1. update branch in database + if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Update(&Branch{ + Name: to, + }); err != nil { + return err + } else if n <= 0 { + return ErrBranchNotExist{ + RepoID: repo.ID, + BranchName: from, + } + } + + // 2. update default branch if needed + isDefault := repo.DefaultBranch == from + if isDefault { + repo.DefaultBranch = to + _, err = sess.ID(repo.ID).Cols("default_branch").Update(repo) + if err != nil { + return err + } + } + + // 3. Update protected branch if needed + protectedBranch, err := GetProtectedBranchRuleByName(ctx, repo.ID, from) if err != nil { return err } - } else { - // some glob protect rules may match this branch - protected, err := IsBranchProtected(ctx, repo.ID, from) + + if protectedBranch != nil { + // there is a protect rule for this branch + protectedBranch.RuleName = to + if _, err = sess.ID(protectedBranch.ID).Cols("branch_name").Update(protectedBranch); err != nil { + return err + } + } else { + // some glob protect rules may match this branch + protected, err := IsBranchProtected(ctx, repo.ID, from) + if err != nil { + return err + } + if protected { + return ErrBranchIsProtected + } + } + + // 4. Update all not merged pull request base branch name + _, err = sess.Table("pull_request").Where("base_repo_id=? AND base_branch=? AND has_merged=?", + repo.ID, from, false). + Update(map[string]any{"base_branch": to}) if err != nil { return err } - if protected { - return ErrBranchIsProtected + + // 4.1 Update all not merged pull request head branch name + if _, err = sess.Table("pull_request").Where("head_repo_id=? AND head_branch=? AND has_merged=?", + repo.ID, from, false). + Update(map[string]any{"head_branch": to}); err != nil { + return err } - } - // 4. Update all not merged pull request base branch name - _, err = sess.Table("pull_request").Where("base_repo_id=? AND base_branch=? AND has_merged=?", - repo.ID, from, false). - Update(map[string]any{"base_branch": to}) - if err != nil { - return err - } + // 5. insert renamed branch record + if err = db.Insert(ctx, &RenamedBranch{ + RepoID: repo.ID, + From: from, + To: to, + }); err != nil { + return err + } - // 4.1 Update all not merged pull request head branch name - if _, err = sess.Table("pull_request").Where("head_repo_id=? AND head_branch=? AND has_merged=?", - repo.ID, from, false). - Update(map[string]any{"head_branch": to}); err != nil { - return err - } - - // 5. insert renamed branch record - renamedBranch := &RenamedBranch{ - RepoID: repo.ID, - From: from, - To: to, - } - err = db.Insert(ctx, renamedBranch) - if err != nil { - return err - } - - // 6. do git action - if err = gitAction(ctx, isDefault); err != nil { - return err - } - - return committer.Commit() + // 6. do git action + return gitAction(ctx, isDefault) + }) } type FindRecentlyPushedNewBranchesOptions struct { diff --git a/models/git/lfs.go b/models/git/lfs.go index c471baf5880..8bba060ff97 100644 --- a/models/git/lfs.go +++ b/models/git/lfs.go @@ -180,29 +180,25 @@ func RemoveLFSMetaObjectByOidFn(ctx context.Context, repoID int64, oid string, f return 0, ErrLFSObjectNotExist } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return 0, err - } - defer committer.Close() + return db.WithTx2(ctx, func(ctx context.Context) (int64, error) { + m := &LFSMetaObject{Pointer: lfs.Pointer{Oid: oid}, RepositoryID: repoID} + if _, err := db.DeleteByBean(ctx, m); err != nil { + return -1, err + } - m := &LFSMetaObject{Pointer: lfs.Pointer{Oid: oid}, RepositoryID: repoID} - if _, err := db.DeleteByBean(ctx, m); err != nil { - return -1, err - } - - count, err := db.CountByBean(ctx, &LFSMetaObject{Pointer: lfs.Pointer{Oid: oid}}) - if err != nil { - return count, err - } - - if fn != nil { - if err := fn(count); err != nil { + count, err := db.CountByBean(ctx, &LFSMetaObject{Pointer: lfs.Pointer{Oid: oid}}) + if err != nil { return count, err } - } - return count, committer.Commit() + if fn != nil { + if err := fn(count); err != nil { + return count, err + } + } + + return count, nil + }) } // GetLFSMetaObjects returns all LFSMetaObjects associated with a repository @@ -243,56 +239,46 @@ func ExistsLFSObject(ctx context.Context, oid string) (bool, error) { // LFSAutoAssociate auto associates accessible LFSMetaObjects func LFSAutoAssociate(ctx context.Context, metas []*LFSMetaObject, user *user_model.User, repoID int64) error { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() + return db.WithTx(ctx, func(ctx context.Context) error { + oids := make([]any, len(metas)) + oidMap := make(map[string]*LFSMetaObject, len(metas)) + for i, meta := range metas { + oids[i] = meta.Oid + oidMap[meta.Oid] = meta + } - sess := db.GetEngine(ctx) + if !user.IsAdmin { + newMetas := make([]*LFSMetaObject, 0, len(metas)) + cond := builder.In( + "`lfs_meta_object`.repository_id", + builder.Select("`repository`.id").From("repository").Where(repo_model.AccessibleRepositoryCondition(user, unit.TypeInvalid)), + ) + if err := db.GetEngine(ctx).Cols("oid").Where(cond).In("oid", oids...).GroupBy("oid").Find(&newMetas); err != nil { + return err + } + if len(newMetas) != len(oidMap) { + return fmt.Errorf("unable collect all LFS objects from database, expected %d, actually %d", len(oidMap), len(newMetas)) + } + for i := range newMetas { + newMetas[i].Size = oidMap[newMetas[i].Oid].Size + newMetas[i].RepositoryID = repoID + } + return db.Insert(ctx, newMetas) + } - oids := make([]any, len(metas)) - oidMap := make(map[string]*LFSMetaObject, len(metas)) - for i, meta := range metas { - oids[i] = meta.Oid - oidMap[meta.Oid] = meta - } - - if !user.IsAdmin { - newMetas := make([]*LFSMetaObject, 0, len(metas)) - cond := builder.In( - "`lfs_meta_object`.repository_id", - builder.Select("`repository`.id").From("repository").Where(repo_model.AccessibleRepositoryCondition(user, unit.TypeInvalid)), - ) - err = sess.Cols("oid").Where(cond).In("oid", oids...).GroupBy("oid").Find(&newMetas) - if err != nil { - return err - } - if len(newMetas) != len(oidMap) { - return fmt.Errorf("unable collect all LFS objects from database, expected %d, actually %d", len(oidMap), len(newMetas)) - } - for i := range newMetas { - newMetas[i].Size = oidMap[newMetas[i].Oid].Size - newMetas[i].RepositoryID = repoID - } - if err = db.Insert(ctx, newMetas); err != nil { - return err - } - } else { // admin can associate any LFS object to any repository, and we do not care about errors (eg: duplicated unique key), // even if error occurs, it won't hurt users and won't make things worse for i := range metas { p := lfs.Pointer{Oid: metas[i].Oid, Size: metas[i].Size} - _, err = sess.Insert(&LFSMetaObject{ + if err := db.Insert(ctx, &LFSMetaObject{ Pointer: p, RepositoryID: repoID, - }) - if err != nil { + }); err != nil { log.Warn("failed to insert LFS meta object %-v for repo_id: %d into database, err=%v", p, repoID, err) } } - } - return committer.Commit() + return nil + }) } // CopyLFS copies LFS data from one repo to another diff --git a/models/git/protected_branch.go b/models/git/protected_branch.go index 55bbe6938cd..511f7563cf5 100644 --- a/models/git/protected_branch.go +++ b/models/git/protected_branch.go @@ -17,12 +17,11 @@ import ( 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/glob" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" - "github.com/gobwas/glob" - "github.com/gobwas/glob/syntax" "xorm.io/builder" ) @@ -77,7 +76,7 @@ func init() { // IsRuleNameSpecial return true if it contains special character func IsRuleNameSpecial(ruleName string) bool { for i := 0; i < len(ruleName); i++ { - if syntax.Special(ruleName[i]) { + if glob.IsSpecialByte(ruleName[i]) { return true } } diff --git a/models/git/protected_branch_list.go b/models/git/protected_branch_list.go index 16f85006723..6b282835a46 100644 --- a/models/git/protected_branch_list.go +++ b/models/git/protected_branch_list.go @@ -8,9 +8,8 @@ import ( "sort" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/optional" - - "github.com/gobwas/glob" ) type ProtectedBranchRules []*ProtectedBranch diff --git a/models/git/protected_tag.go b/models/git/protected_tag.go index 9a6646c742c..95642df5932 100644 --- a/models/git/protected_tag.go +++ b/models/git/protected_tag.go @@ -11,9 +11,8 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/timeutil" - - "github.com/gobwas/glob" ) // ProtectedTag struct diff --git a/models/issues/assignees.go b/models/issues/assignees.go index efd992cda2b..54f995dd2e9 100644 --- a/models/issues/assignees.go +++ b/models/issues/assignees.go @@ -91,18 +91,10 @@ func GetAssignedIssues(ctx context.Context, opts *AssignedIssuesOptions) ([]*Iss // ToggleIssueAssignee changes a user between assigned and not assigned for this issue, and make issue comment for it. func ToggleIssueAssignee(ctx context.Context, issue *Issue, doer *user_model.User, assigneeID int64) (removed bool, comment *Comment, err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return false, nil, err - } - defer committer.Close() - - removed, comment, err = toggleIssueAssignee(ctx, issue, doer, assigneeID, false) - if err != nil { - return false, nil, err - } - - if err := committer.Commit(); err != nil { + if err := db.WithTx(ctx, func(ctx context.Context) error { + removed, comment, err = toggleIssueAssignee(ctx, issue, doer, assigneeID, false) + return err + }); err != nil { return false, nil, err } diff --git a/models/issues/comment.go b/models/issues/comment.go index a366b7c6a9c..3a4049700de 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -279,8 +279,8 @@ type Comment struct { DependentIssue *Issue `xorm:"-"` CommitID int64 - Line int64 // - previous line / + proposed line - TreePath string + Line int64 // - previous line / + proposed line + TreePath string `xorm:"VARCHAR(4000)"` // SQLServer only supports up to 4000 Content string `xorm:"LONGTEXT"` ContentVersion int `xorm:"NOT NULL DEFAULT 0"` RenderedContent template.HTML `xorm:"-"` diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go index 1c168174918..553e99aece2 100644 --- a/models/issues/issue_update.go +++ b/models/issues/issue_update.go @@ -415,37 +415,28 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue // NewIssue creates new issue with labels for repository. // The title will be cut off at 255 characters if it's longer than 255 characters. func NewIssue(ctx context.Context, repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - idx, err := db.GetNextResourceIndex(ctx, "issue_index", repo.ID) - if err != nil { - return fmt.Errorf("generate issue index failed: %w", err) - } - - issue.Index = idx - issue.Title = util.EllipsisDisplayString(issue.Title, 255) - - if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{ - Repo: repo, - Issue: issue, - LabelIDs: labelIDs, - Attachments: uuids, - }); err != nil { - if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) { - return err + return db.WithTx(ctx, func(ctx context.Context) error { + idx, err := db.GetNextResourceIndex(ctx, "issue_index", repo.ID) + if err != nil { + return fmt.Errorf("generate issue index failed: %w", err) } - return fmt.Errorf("newIssue: %w", err) - } - if err = committer.Commit(); err != nil { - return fmt.Errorf("Commit: %w", err) - } + issue.Index = idx + issue.Title = util.EllipsisDisplayString(issue.Title, 255) - return nil + if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{ + Repo: repo, + Issue: issue, + LabelIDs: labelIDs, + Attachments: uuids, + }); err != nil { + if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) { + return err + } + return fmt.Errorf("newIssue: %w", err) + } + return nil + }) } // UpdateIssueMentions updates issue-user relations for mentioned users. diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 88967a8b870..1b1558f39d1 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -393,6 +393,7 @@ func prepareMigrationTasks() []*migration { // Gitea 1.24.0 ends at database version 321 newMigration(321, "Use LONGTEXT for some columns and fix review_state.updated_files column", v1_25.UseLongTextInSomeColumnsAndFixBugs), + newMigration(322, "Extend comment tree_path length limit", v1_25.ExtendCommentTreePathLength), } return preparedMigrations } diff --git a/models/migrations/v1_11/v111.go b/models/migrations/v1_11/v111.go index c27465f0518..2634906565e 100644 --- a/models/migrations/v1_11/v111.go +++ b/models/migrations/v1_11/v111.go @@ -408,7 +408,7 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { official, err := isOfficialReviewer(sess, review.IssueID, reviewer) if err != nil { - // Branch might not be proteced or other error, ignore it. + // Branch might not be protected or other error, ignore it. continue } review.Official = official diff --git a/models/migrations/v1_12/v128.go b/models/migrations/v1_12/v128.go index d2e8b86a3d9..34746dcdc42 100644 --- a/models/migrations/v1_12/v128.go +++ b/models/migrations/v1_12/v128.go @@ -12,6 +12,7 @@ import ( "time" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -83,17 +84,17 @@ func FixMergeBase(ctx context.Context, x *xorm.Engine) error { if !pr.HasMerged { var err error - pr.MergeBase, _, err = git.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).RunStdString(ctx, &git.RunOpts{Dir: repoPath}) + pr.MergeBase, _, err = gitcmd.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if err != nil { var err2 error - pr.MergeBase, _, err2 = git.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix+pr.BaseBranch).RunStdString(ctx, &git.RunOpts{Dir: repoPath}) + pr.MergeBase, _, err2 = gitcmd.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix+pr.BaseBranch).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if err2 != nil { log.Error("Unable to get merge base for PR ID %d, Index %d in %s/%s. Error: %v & %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err, err2) continue } } } else { - parentsString, _, err := git.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(ctx, &git.RunOpts{Dir: repoPath}) + parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if err != nil { log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err) continue @@ -105,9 +106,9 @@ func FixMergeBase(ctx context.Context, x *xorm.Engine) error { refs := append([]string{}, parents[1:]...) refs = append(refs, gitRefName) - cmd := git.NewCommand("merge-base").AddDashesAndList(refs...) + cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...) - pr.MergeBase, _, err = cmd.RunStdString(ctx, &git.RunOpts{Dir: repoPath}) + pr.MergeBase, _, err = cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if err != nil { log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err) continue diff --git a/models/migrations/v1_12/v134.go b/models/migrations/v1_12/v134.go index 189d80cc523..d31cc3abdb3 100644 --- a/models/migrations/v1_12/v134.go +++ b/models/migrations/v1_12/v134.go @@ -11,7 +11,7 @@ import ( "strings" "time" - "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -80,7 +80,7 @@ func RefixMergeBase(ctx context.Context, x *xorm.Engine) error { gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index) - parentsString, _, err := git.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(ctx, &git.RunOpts{Dir: repoPath}) + parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if err != nil { log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err) continue @@ -93,9 +93,9 @@ func RefixMergeBase(ctx context.Context, x *xorm.Engine) error { // we should recalculate refs := append([]string{}, parents[1:]...) refs = append(refs, gitRefName) - cmd := git.NewCommand("merge-base").AddDashesAndList(refs...) + cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...) - pr.MergeBase, _, err = cmd.RunStdString(ctx, &git.RunOpts{Dir: repoPath}) + pr.MergeBase, _, err = cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if err != nil { log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err) continue diff --git a/models/migrations/v1_25/v322.go b/models/migrations/v1_25/v322.go new file mode 100644 index 00000000000..32dae9945ae --- /dev/null +++ b/models/migrations/v1_25/v322.go @@ -0,0 +1,28 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_25 + +import ( + "code.gitea.io/gitea/models/migrations/base" + + "xorm.io/xorm" + "xorm.io/xorm/schemas" +) + +func ExtendCommentTreePathLength(x *xorm.Engine) error { + dbType := x.Dialect().URI().DBType + if dbType == schemas.SQLITE { // For SQLITE, varchar or char will always be represented as TEXT + return nil + } + + return base.ModifyColumn(x, "comment", &schemas.Column{ + Name: "tree_path", + SQLType: schemas.SQLType{ + Name: "VARCHAR", + }, + Length: 4000, + Nullable: true, // To keep compatible as nullable + DefaultIsEmpty: true, + }) +} diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go index 7de43ecd07c..678b18442ee 100644 --- a/models/perm/access/repo_permission.go +++ b/models/perm/access/repo_permission.go @@ -348,10 +348,8 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use for _, u := range repo.Units { for _, team := range teams { - unitAccessMode := minAccessMode - if teamMode, exist := team.UnitAccessModeEx(ctx, u.Type); exist { - unitAccessMode = max(perm.unitsMode[u.Type], unitAccessMode, teamMode) - } + teamMode, _ := team.UnitAccessModeEx(ctx, u.Type) + unitAccessMode := max(perm.unitsMode[u.Type], minAccessMode, teamMode) perm.unitsMode[u.Type] = unitAccessMode } } diff --git a/models/perm/access/repo_permission_test.go b/models/perm/access/repo_permission_test.go index c8675b1ded5..d81dfba288e 100644 --- a/models/perm/access/repo_permission_test.go +++ b/models/perm/access/repo_permission_test.go @@ -197,4 +197,37 @@ func TestGetUserRepoPermission(t *testing.T) { assert.Equal(t, perm_model.AccessModeWrite, perm.unitsMode[unit.TypeCode]) assert.Equal(t, perm_model.AccessModeRead, perm.unitsMode[unit.TypeIssues]) }) + + repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) // org private repo, same org as repo 32 + require.NoError(t, repo3.LoadOwner(ctx)) + require.True(t, repo3.Owner.IsOrganization()) + require.NoError(t, db.TruncateBeans(ctx, &organization.TeamUnit{}, &Access{})) // The user has access set of that repo, remove it, it is useless for our test + require.NoError(t, db.Insert(ctx, &organization.TeamRepo{OrgID: org.ID, TeamID: team.ID, RepoID: repo3.ID})) + t.Run("DoerWithNoopTeamOnPrivateRepo", func(t *testing.T) { + perm, err := GetUserRepoPermission(ctx, repo3, user) + require.NoError(t, err) + assert.Equal(t, perm_model.AccessModeNone, perm.AccessMode) + assert.Equal(t, perm_model.AccessModeNone, perm.unitsMode[unit.TypeCode]) + assert.Equal(t, perm_model.AccessModeNone, perm.unitsMode[unit.TypeIssues]) + }) + + require.NoError(t, db.Insert(ctx, &organization.TeamUnit{OrgID: org.ID, TeamID: team.ID, Type: unit.TypeCode, AccessMode: perm_model.AccessModeNone})) + require.NoError(t, db.Insert(ctx, &organization.TeamUnit{OrgID: org.ID, TeamID: team.ID, Type: unit.TypeIssues, AccessMode: perm_model.AccessModeRead})) + t.Run("DoerWithReadIssueTeamOnPrivateRepo", func(t *testing.T) { + perm, err := GetUserRepoPermission(ctx, repo3, user) + require.NoError(t, err) + assert.Equal(t, perm_model.AccessModeNone, perm.AccessMode) + assert.Equal(t, perm_model.AccessModeNone, perm.unitsMode[unit.TypeCode]) + assert.Equal(t, perm_model.AccessModeRead, perm.unitsMode[unit.TypeIssues]) + }) + + require.NoError(t, db.Insert(ctx, repo_model.Collaboration{RepoID: repo3.ID, UserID: user.ID, Mode: perm_model.AccessModeWrite})) + require.NoError(t, db.Insert(ctx, Access{RepoID: repo3.ID, UserID: user.ID, Mode: perm_model.AccessModeWrite})) + t.Run("DoerWithReadIssueTeamAndWriteCollaboratorOnPrivateRepo", func(t *testing.T) { + perm, err := GetUserRepoPermission(ctx, repo3, user) + require.NoError(t, err) + assert.Equal(t, perm_model.AccessModeWrite, perm.AccessMode) + assert.Equal(t, perm_model.AccessModeWrite, perm.unitsMode[unit.TypeCode]) + assert.Equal(t, perm_model.AccessModeWrite, perm.unitsMode[unit.TypeIssues]) + }) } diff --git a/models/user/email_address.go b/models/user/email_address.go index cb423945f8a..67aa1bdd822 100644 --- a/models/user/email_address.go +++ b/models/user/email_address.go @@ -442,58 +442,53 @@ func SearchEmails(ctx context.Context, opts *SearchEmailOptions) ([]*SearchEmail // ActivateUserEmail will change the activated state of an email address, // either primary or secondary (all in the email_address table) func ActivateUserEmail(ctx context.Context, userID int64, email string, activate bool) (err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - // Activate/deactivate a user's secondary email address - // First check if there's another user active with the same address - addr, exist, err := db.Get[EmailAddress](ctx, builder.Eq{"uid": userID, "lower_email": strings.ToLower(email)}) - if err != nil { - return err - } else if !exist { - return fmt.Errorf("no such email: %d (%s)", userID, email) - } - - if addr.IsActivated == activate { - // Already in the desired state; no action - return nil - } - if activate { - if used, err := IsEmailActive(ctx, email, addr.ID); err != nil { - return fmt.Errorf("unable to check isEmailActive() for %s: %w", email, err) - } else if used { - return ErrEmailAlreadyUsed{Email: email} - } - } - if err = updateActivation(ctx, addr, activate); err != nil { - return fmt.Errorf("unable to updateActivation() for %d:%s: %w", addr.ID, addr.Email, err) - } - - // Activate/deactivate a user's primary email address and account - if addr.IsPrimary { - user, exist, err := db.Get[User](ctx, builder.Eq{"id": userID}) + return db.WithTx(ctx, func(ctx context.Context) error { + // Activate/deactivate a user's secondary email address + // First check if there's another user active with the same address + addr, exist, err := db.Get[EmailAddress](ctx, builder.Eq{"uid": userID, "lower_email": strings.ToLower(email)}) if err != nil { return err - } else if !exist || !strings.EqualFold(user.Email, email) { - return fmt.Errorf("no user with ID: %d and Email: %s", userID, email) + } else if !exist { + return fmt.Errorf("no such email: %d (%s)", userID, email) } - // The user's activation state should be synchronized with the primary email - if user.IsActive != activate { - user.IsActive = activate - if user.Rands, err = GetUserSalt(); err != nil { - return fmt.Errorf("unable to generate salt: %w", err) - } - if err = UpdateUserCols(ctx, user, "is_active", "rands"); err != nil { - return fmt.Errorf("unable to updateUserCols() for user ID: %d: %w", userID, err) + if addr.IsActivated == activate { + // Already in the desired state; no action + return nil + } + if activate { + if used, err := IsEmailActive(ctx, email, addr.ID); err != nil { + return fmt.Errorf("unable to check isEmailActive() for %s: %w", email, err) + } else if used { + return ErrEmailAlreadyUsed{Email: email} } } - } + if err = updateActivation(ctx, addr, activate); err != nil { + return fmt.Errorf("unable to updateActivation() for %d:%s: %w", addr.ID, addr.Email, err) + } - return committer.Commit() + // Activate/deactivate a user's primary email address and account + if addr.IsPrimary { + user, exist, err := db.Get[User](ctx, builder.Eq{"id": userID}) + if err != nil { + return err + } else if !exist || !strings.EqualFold(user.Email, email) { + return fmt.Errorf("no user with ID: %d and Email: %s", userID, email) + } + + // The user's activation state should be synchronized with the primary email + if user.IsActive != activate { + user.IsActive = activate + if user.Rands, err = GetUserSalt(); err != nil { + return fmt.Errorf("unable to generate salt: %w", err) + } + if err = UpdateUserCols(ctx, user, "is_active", "rands"); err != nil { + return fmt.Errorf("unable to updateUserCols() for user ID: %d: %w", userID, err) + } + } + } + return nil + }) } // validateEmailBasic checks whether the email complies with the rules diff --git a/models/user/setting_options.go b/models/user/setting_options.go index 7be50393296..6d37ef63d55 100644 --- a/models/user/setting_options.go +++ b/models/user/setting_options.go @@ -8,7 +8,7 @@ const ( SettingsKeyHiddenCommentTypes = "issue.hidden_comment_types" // SettingsKeyDiffWhitespaceBehavior is the setting key for whitespace behavior of diff SettingsKeyDiffWhitespaceBehavior = "diff.whitespace_behaviour" - // SettingsKeyShowOutdatedComments is the setting key wether or not to show outdated comments in PRs + // SettingsKeyShowOutdatedComments is the setting key whether or not to show outdated comments in PRs SettingsKeyShowOutdatedComments = "comment_code.show_outdated" // UserActivityPubPrivPem is user's private key diff --git a/models/user/user.go b/models/user/user.go index ae500f3a1f2..6143992a253 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -716,90 +716,82 @@ func createUser(ctx context.Context, u *User, meta *Meta, createdByAdmin bool, o } } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - isExist, err := IsUserExist(ctx, 0, u.Name) - if err != nil { - return err - } else if isExist { - return ErrUserAlreadyExist{u.Name} - } - - isExist, err = IsEmailUsed(ctx, u.Email) - if err != nil { - return err - } else if isExist { - return ErrEmailAlreadyUsed{ - Email: u.Email, + return db.WithTx(ctx, func(ctx context.Context) error { + isExist, err := IsUserExist(ctx, 0, u.Name) + if err != nil { + return err + } else if isExist { + return ErrUserAlreadyExist{u.Name} } - } - // prepare for database + isExist, err = IsEmailUsed(ctx, u.Email) + if err != nil { + return err + } else if isExist { + return ErrEmailAlreadyUsed{ + Email: u.Email, + } + } - u.LowerName = strings.ToLower(u.Name) - u.AvatarEmail = u.Email - if u.Rands, err = GetUserSalt(); err != nil { - return err - } - if u.Passwd != "" { - if err = u.SetPassword(u.Passwd); err != nil { + // prepare for database + + u.LowerName = strings.ToLower(u.Name) + u.AvatarEmail = u.Email + if u.Rands, err = GetUserSalt(); err != nil { return err } - } else { - u.Salt = "" - u.PasswdHashAlgo = "" - } + if u.Passwd != "" { + if err = u.SetPassword(u.Passwd); err != nil { + return err + } + } else { + u.Salt = "" + u.PasswdHashAlgo = "" + } - // save changes to database + // save changes to database - if err = DeleteUserRedirect(ctx, u.Name); err != nil { - return err - } - - if u.CreatedUnix == 0 { - // Caller expects auto-time for creation & update timestamps. - err = db.Insert(ctx, u) - } else { - // Caller sets the timestamps themselves. They are responsible for ensuring - // both `CreatedUnix` and `UpdatedUnix` are set appropriately. - _, err = db.GetEngine(ctx).NoAutoTime().Insert(u) - } - if err != nil { - return err - } - - if setting.RecordUserSignupMetadata { - // insert initial IP and UserAgent - if err = SetUserSetting(ctx, u.ID, SignupIP, meta.InitialIP); err != nil { + if err = DeleteUserRedirect(ctx, u.Name); err != nil { return err } - // trim user agent string to a reasonable length, if necessary - userAgent := strings.TrimSpace(meta.InitialUserAgent) - if len(userAgent) > 255 { - userAgent = userAgent[:255] + if u.CreatedUnix == 0 { + // Caller expects auto-time for creation & update timestamps. + err = db.Insert(ctx, u) + } else { + // Caller sets the timestamps themselves. They are responsible for ensuring + // both `CreatedUnix` and `UpdatedUnix` are set appropriately. + _, err = db.GetEngine(ctx).NoAutoTime().Insert(u) } - if err = SetUserSetting(ctx, u.ID, SignupUserAgent, userAgent); err != nil { + if err != nil { return err } - } - // insert email address - if err := db.Insert(ctx, &EmailAddress{ - UID: u.ID, - Email: u.Email, - LowerEmail: strings.ToLower(u.Email), - IsActivated: u.IsActive, - IsPrimary: true, - }); err != nil { - return err - } + if setting.RecordUserSignupMetadata { + // insert initial IP and UserAgent + if err = SetUserSetting(ctx, u.ID, SignupIP, meta.InitialIP); err != nil { + return err + } - return committer.Commit() + // trim user agent string to a reasonable length, if necessary + userAgent := strings.TrimSpace(meta.InitialUserAgent) + if len(userAgent) > 255 { + userAgent = userAgent[:255] + } + if err = SetUserSetting(ctx, u.ID, SignupUserAgent, userAgent); err != nil { + return err + } + } + + // insert email address + return db.Insert(ctx, &EmailAddress{ + UID: u.ID, + Email: u.Email, + LowerEmail: strings.ToLower(u.Email), + IsActivated: u.IsActive, + IsPrimary: true, + }) + }) } // ErrDeleteLastAdminUser represents a "DeleteLastAdminUser" kind of error. diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go index 27bcafa6497..5fc530b63e5 100644 --- a/modules/actions/workflows.go +++ b/modules/actions/workflows.go @@ -10,11 +10,11 @@ import ( "strings" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" - "github.com/gobwas/glob" "github.com/nektos/act/pkg/jobparser" "github.com/nektos/act/pkg/model" "github.com/nektos/act/pkg/workflowpattern" diff --git a/modules/avatar/identicon/identicon.go b/modules/avatar/identicon/identicon.go index ee92416a536..19f87da85af 100644 --- a/modules/avatar/identicon/identicon.go +++ b/modules/avatar/identicon/identicon.go @@ -70,7 +70,7 @@ func (i *Identicon) render(c, b1, b2, b1Angle, b2Angle, foreColor int) image.Ima /* # Algorithm -Origin: An image is splitted into 9 areas +Origin: An image is split into 9 areas ``` ------------- diff --git a/modules/git/attribute/batch.go b/modules/git/attribute/batch.go index 4e31fda5753..9f805d55c51 100644 --- a/modules/git/attribute/batch.go +++ b/modules/git/attribute/batch.go @@ -12,6 +12,7 @@ import ( "time" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" ) @@ -23,7 +24,7 @@ type BatchChecker struct { stdOut *nulSeparatedAttributeWriter ctx context.Context cancel context.CancelFunc - cmd *git.Command + cmd *gitcmd.Command } // NewBatchChecker creates a check attribute reader for the current repository and provided commit ID @@ -76,7 +77,7 @@ func NewBatchChecker(repo *git.Repository, treeish string, attributes []string) _ = lw.Close() }() stdErr := new(bytes.Buffer) - err := cmd.Run(ctx, &git.RunOpts{ + err := cmd.Run(ctx, &gitcmd.RunOpts{ Env: envs, Dir: repo.Path, Stdin: stdinReader, diff --git a/modules/git/attribute/checker.go b/modules/git/attribute/checker.go index 167b31416e1..4b313adf377 100644 --- a/modules/git/attribute/checker.go +++ b/modules/git/attribute/checker.go @@ -11,12 +11,13 @@ import ( "os" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" ) -func checkAttrCommand(gitRepo *git.Repository, treeish string, filenames, attributes []string) (*git.Command, []string, func(), error) { +func checkAttrCommand(gitRepo *git.Repository, treeish string, filenames, attributes []string) (*gitcmd.Command, []string, func(), error) { cancel := func() {} envs := []string{"GIT_FLUSH=1"} - cmd := git.NewCommand("check-attr", "-z") + cmd := gitcmd.NewCommand("check-attr", "-z") if len(attributes) == 0 { cmd.AddArguments("--all") } @@ -70,7 +71,7 @@ func CheckAttributes(ctx context.Context, gitRepo *git.Repository, treeish strin stdOut := new(bytes.Buffer) stdErr := new(bytes.Buffer) - if err := cmd.Run(ctx, &git.RunOpts{ + if err := cmd.Run(ctx, &gitcmd.RunOpts{ Env: append(os.Environ(), envs...), Dir: gitRepo.Path, Stdout: stdOut, diff --git a/modules/git/batch_reader.go b/modules/git/batch_reader.go index 7bbab76bb82..f09f4144c89 100644 --- a/modules/git/batch_reader.go +++ b/modules/git/batch_reader.go @@ -12,6 +12,7 @@ import ( "strconv" "strings" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" "github.com/djherbis/buffer" @@ -29,13 +30,13 @@ type WriteCloserError interface { // This is needed otherwise the git cat-file will hang for invalid repositories. func ensureValidGitRepository(ctx context.Context, repoPath string) error { stderr := strings.Builder{} - err := NewCommand("rev-parse"). - Run(ctx, &RunOpts{ + err := gitcmd.NewCommand("rev-parse"). + Run(ctx, &gitcmd.RunOpts{ Dir: repoPath, Stderr: &stderr, }) if err != nil { - return ConcatenateError(err, (&stderr).String()) + return gitcmd.ConcatenateError(err, (&stderr).String()) } return nil } @@ -61,8 +62,8 @@ func catFileBatchCheck(ctx context.Context, repoPath string) (WriteCloserError, go func() { stderr := strings.Builder{} - err := NewCommand("cat-file", "--batch-check"). - Run(ctx, &RunOpts{ + err := gitcmd.NewCommand("cat-file", "--batch-check"). + Run(ctx, &gitcmd.RunOpts{ Dir: repoPath, Stdin: batchStdinReader, Stdout: batchStdoutWriter, @@ -71,8 +72,8 @@ func catFileBatchCheck(ctx context.Context, repoPath string) (WriteCloserError, UseContextTimeout: true, }) if err != nil { - _ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String())) - _ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String())) + _ = batchStdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String())) + _ = batchStdinReader.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String())) } else { _ = batchStdoutWriter.Close() _ = batchStdinReader.Close() @@ -109,8 +110,8 @@ func catFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi go func() { stderr := strings.Builder{} - err := NewCommand("cat-file", "--batch"). - Run(ctx, &RunOpts{ + err := gitcmd.NewCommand("cat-file", "--batch"). + Run(ctx, &gitcmd.RunOpts{ Dir: repoPath, Stdin: batchStdinReader, Stdout: batchStdoutWriter, @@ -119,8 +120,8 @@ func catFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi UseContextTimeout: true, }) if err != nil { - _ = batchStdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String())) - _ = batchStdinReader.CloseWithError(ConcatenateError(err, (&stderr).String())) + _ = batchStdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String())) + _ = batchStdinReader.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String())) } else { _ = batchStdoutWriter.Close() _ = batchStdinReader.Close() diff --git a/modules/git/blame.go b/modules/git/blame.go index db10df129e2..50cadc41c22 100644 --- a/modules/git/blame.go +++ b/modules/git/blame.go @@ -10,6 +10,7 @@ import ( "io" "os" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" ) @@ -141,7 +142,7 @@ func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath } }() - cmd := NewCommand("blame", "--porcelain") + cmd := gitcmd.NewCommand("blame", "--porcelain") if DefaultFeatures().CheckVersionAtLeast("2.23") && !bypassBlameIgnore { ignoreRevsFileName, ignoreRevsFileCleanup, err = tryCreateBlameIgnoreRevsFile(commit) @@ -165,7 +166,7 @@ func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath go func() { stderr := bytes.Buffer{} // TODO: it doesn't work for directories (the directories shouldn't be "blamed"), and the "err" should be returned by "Read" but not by "Close" - err := cmd.Run(ctx, &RunOpts{ + err := cmd.Run(ctx, &gitcmd.RunOpts{ UseContextTimeout: true, Dir: repoPath, Stdout: stdout, diff --git a/modules/git/commit.go b/modules/git/commit.go index 0dc894d3ab6..a0c5955ae8b 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -14,6 +14,7 @@ import ( "strconv" "strings" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" ) @@ -87,12 +88,12 @@ func (c *Commit) GetCommitByPath(relpath string) (*Commit, error) { // AddChanges marks local changes to be ready for commit. func AddChanges(ctx context.Context, repoPath string, all bool, files ...string) error { - cmd := NewCommand().AddArguments("add") + cmd := gitcmd.NewCommand().AddArguments("add") if all { cmd.AddArguments("--all") } cmd.AddDashesAndList(files...) - _, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath}) + _, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) return err } @@ -106,7 +107,7 @@ type CommitChangesOptions struct { // CommitChanges commits local changes with given committer, author and message. // If author is nil, it will be the same as committer. func CommitChanges(ctx context.Context, repoPath string, opts CommitChangesOptions) error { - cmd := NewCommand() + cmd := gitcmd.NewCommand() if opts.Committer != nil { cmd.AddOptionValues("-c", "user.name="+opts.Committer.Name) cmd.AddOptionValues("-c", "user.email="+opts.Committer.Email) @@ -121,7 +122,7 @@ func CommitChanges(ctx context.Context, repoPath string, opts CommitChangesOptio } cmd.AddOptionFormat("--message=%s", opts.Message) - _, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath}) + _, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) // No stderr but exit status 1 means nothing to commit. if err != nil && err.Error() == "exit status 1" { return nil @@ -131,7 +132,7 @@ func CommitChanges(ctx context.Context, repoPath string, opts CommitChangesOptio // AllCommitsCount returns count of all commits in repository func AllCommitsCount(ctx context.Context, repoPath string, hidePRRefs bool, files ...string) (int64, error) { - cmd := NewCommand("rev-list") + cmd := gitcmd.NewCommand("rev-list") if hidePRRefs { cmd.AddArguments("--exclude=" + PullPrefix + "*") } @@ -140,7 +141,7 @@ func AllCommitsCount(ctx context.Context, repoPath string, hidePRRefs bool, file cmd.AddDashesAndList(files...) } - stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath}) + stdout, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if err != nil { return 0, err } @@ -160,7 +161,7 @@ type CommitsCountOptions struct { // CommitsCount returns number of total commits of until given revision. func CommitsCount(ctx context.Context, opts CommitsCountOptions) (int64, error) { - cmd := NewCommand("rev-list", "--count") + cmd := gitcmd.NewCommand("rev-list", "--count") cmd.AddDynamicArguments(opts.Revision...) @@ -172,7 +173,7 @@ func CommitsCount(ctx context.Context, opts CommitsCountOptions) (int64, error) cmd.AddDashesAndList(opts.RelPath...) } - stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: opts.RepoPath}) + stdout, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: opts.RepoPath}) if err != nil { return 0, err } @@ -207,7 +208,7 @@ func (c *Commit) HasPreviousCommit(objectID ObjectID) (bool, error) { return false, nil } - _, _, err := NewCommand("merge-base", "--is-ancestor").AddDynamicArguments(that, this).RunStdString(c.repo.Ctx, &RunOpts{Dir: c.repo.Path}) + _, _, err := gitcmd.NewCommand("merge-base", "--is-ancestor").AddDynamicArguments(that, this).RunStdString(c.repo.Ctx, &gitcmd.RunOpts{Dir: c.repo.Path}) if err == nil { return true, nil } @@ -348,12 +349,12 @@ func (c *Commit) GetFileContent(filename string, limit int) (string, error) { // GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only') func (c *Commit) GetBranchName() (string, error) { - cmd := NewCommand("name-rev") + cmd := gitcmd.NewCommand("name-rev") if DefaultFeatures().CheckVersionAtLeast("2.13.0") { cmd.AddArguments("--exclude", "refs/tags/*") } cmd.AddArguments("--name-only", "--no-undefined").AddDynamicArguments(c.ID.String()) - data, _, err := cmd.RunStdString(c.repo.Ctx, &RunOpts{Dir: c.repo.Path}) + data, _, err := cmd.RunStdString(c.repo.Ctx, &gitcmd.RunOpts{Dir: c.repo.Path}) if err != nil { // handle special case where git can not describe commit if strings.Contains(err.Error(), "cannot describe") { @@ -431,14 +432,14 @@ func GetCommitFileStatus(ctx context.Context, repoPath, commitID string) (*Commi }() stderr := new(bytes.Buffer) - err := NewCommand("log", "--name-status", "-m", "--pretty=format:", "--first-parent", "--no-renames", "-z", "-1").AddDynamicArguments(commitID).Run(ctx, &RunOpts{ + err := gitcmd.NewCommand("log", "--name-status", "-m", "--pretty=format:", "--first-parent", "--no-renames", "-z", "-1").AddDynamicArguments(commitID).Run(ctx, &gitcmd.RunOpts{ Dir: repoPath, Stdout: w, Stderr: stderr, }) w.Close() // Close writer to exit parsing goroutine if err != nil { - return nil, ConcatenateError(err, stderr.String()) + return nil, gitcmd.ConcatenateError(err, stderr.String()) } <-done @@ -447,7 +448,7 @@ func GetCommitFileStatus(ctx context.Context, repoPath, commitID string) (*Commi // GetFullCommitID returns full length (40) of commit ID by given short SHA in a repository. func GetFullCommitID(ctx context.Context, repoPath, shortID string) (string, error) { - commitID, _, err := NewCommand("rev-parse").AddDynamicArguments(shortID).RunStdString(ctx, &RunOpts{Dir: repoPath}) + commitID, _, err := gitcmd.NewCommand("rev-parse").AddDynamicArguments(shortID).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if err != nil { if strings.Contains(err.Error(), "exit status 128") { return "", ErrNotExist{shortID, ""} diff --git a/modules/git/config.go b/modules/git/config.go index 3a717908b9b..2eafe971b39 100644 --- a/modules/git/config.go +++ b/modules/git/config.go @@ -11,13 +11,14 @@ import ( "runtime" "strings" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/setting" ) // syncGitConfig only modifies gitconfig, won't change global variables (otherwise there will be data-race problem) func syncGitConfig(ctx context.Context) (err error) { - if err = os.MkdirAll(HomeDir(), os.ModePerm); err != nil { - return fmt.Errorf("unable to prepare git home directory %s, err: %w", HomeDir(), err) + if err = os.MkdirAll(gitcmd.HomeDir(), os.ModePerm); err != nil { + return fmt.Errorf("unable to prepare git home directory %s, err: %w", gitcmd.HomeDir(), err) } // first, write user's git config options to git config file @@ -117,8 +118,8 @@ func syncGitConfig(ctx context.Context) (err error) { } func configSet(ctx context.Context, key, value string) error { - stdout, _, err := NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx, nil) - if err != nil && !IsErrorExitCode(err, 1) { + stdout, _, err := gitcmd.NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx, nil) + if err != nil && !gitcmd.IsErrorExitCode(err, 1) { return fmt.Errorf("failed to get git config %s, err: %w", key, err) } @@ -127,7 +128,7 @@ func configSet(ctx context.Context, key, value string) error { return nil } - _, _, err = NewCommand("config", "--global").AddDynamicArguments(key, value).RunStdString(ctx, nil) + _, _, err = gitcmd.NewCommand("config", "--global").AddDynamicArguments(key, value).RunStdString(ctx, nil) if err != nil { return fmt.Errorf("failed to set git global config %s, err: %w", key, err) } @@ -136,14 +137,14 @@ func configSet(ctx context.Context, key, value string) error { } func configSetNonExist(ctx context.Context, key, value string) error { - _, _, err := NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx, nil) + _, _, err := gitcmd.NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx, nil) if err == nil { // already exist return nil } - if IsErrorExitCode(err, 1) { + if gitcmd.IsErrorExitCode(err, 1) { // not exist, set new config - _, _, err = NewCommand("config", "--global").AddDynamicArguments(key, value).RunStdString(ctx, nil) + _, _, err = gitcmd.NewCommand("config", "--global").AddDynamicArguments(key, value).RunStdString(ctx, nil) if err != nil { return fmt.Errorf("failed to set git global config %s, err: %w", key, err) } @@ -154,14 +155,14 @@ func configSetNonExist(ctx context.Context, key, value string) error { } func configAddNonExist(ctx context.Context, key, value string) error { - _, _, err := NewCommand("config", "--global", "--get").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(ctx, nil) + _, _, err := gitcmd.NewCommand("config", "--global", "--get").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(ctx, nil) if err == nil { // already exist return nil } - if IsErrorExitCode(err, 1) { + if gitcmd.IsErrorExitCode(err, 1) { // not exist, add new config - _, _, err = NewCommand("config", "--global", "--add").AddDynamicArguments(key, value).RunStdString(ctx, nil) + _, _, err = gitcmd.NewCommand("config", "--global", "--add").AddDynamicArguments(key, value).RunStdString(ctx, nil) if err != nil { return fmt.Errorf("failed to add git global config %s, err: %w", key, err) } @@ -171,16 +172,16 @@ func configAddNonExist(ctx context.Context, key, value string) error { } func configUnsetAll(ctx context.Context, key, value string) error { - _, _, err := NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx, nil) + _, _, err := gitcmd.NewCommand("config", "--global", "--get").AddDynamicArguments(key).RunStdString(ctx, nil) if err == nil { // exist, need to remove - _, _, err = NewCommand("config", "--global", "--unset-all").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(ctx, nil) + _, _, err = gitcmd.NewCommand("config", "--global", "--unset-all").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(ctx, nil) if err != nil { return fmt.Errorf("failed to unset git global config %s, err: %w", key, err) } return nil } - if IsErrorExitCode(err, 1) { + if gitcmd.IsErrorExitCode(err, 1) { // not exist return nil } diff --git a/modules/git/config_test.go b/modules/git/config_test.go index 911e0e01845..237feb67d54 100644 --- a/modules/git/config_test.go +++ b/modules/git/config_test.go @@ -8,13 +8,14 @@ import ( "strings" "testing" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/setting" "github.com/stretchr/testify/assert" ) func gitConfigContains(sub string) bool { - if b, err := os.ReadFile(HomeDir() + "/.gitconfig"); err == nil { + if b, err := os.ReadFile(gitcmd.HomeDir() + "/.gitconfig"); err == nil { return strings.Contains(string(b), sub) } return false diff --git a/modules/git/diff.go b/modules/git/diff.go index 35d115be0e5..d185cc92776 100644 --- a/modules/git/diff.go +++ b/modules/git/diff.go @@ -14,6 +14,7 @@ import ( "strconv" "strings" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" ) @@ -34,8 +35,8 @@ func GetRawDiff(repo *Repository, commitID string, diffType RawDiffType, writer // GetReverseRawDiff dumps the reverse diff results of repository in given commit ID to io.Writer. func GetReverseRawDiff(ctx context.Context, repoPath, commitID string, writer io.Writer) error { stderr := new(bytes.Buffer) - cmd := NewCommand("show", "--pretty=format:revert %H%n", "-R").AddDynamicArguments(commitID) - if err := cmd.Run(ctx, &RunOpts{ + cmd := gitcmd.NewCommand("show", "--pretty=format:revert %H%n", "-R").AddDynamicArguments(commitID) + if err := cmd.Run(ctx, &gitcmd.RunOpts{ Dir: repoPath, Stdout: writer, Stderr: stderr, @@ -56,7 +57,7 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff files = append(files, file) } - cmd := NewCommand() + cmd := gitcmd.NewCommand() switch diffType { case RawDiffNormal: if len(startCommit) != 0 { @@ -89,7 +90,7 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff } stderr := new(bytes.Buffer) - if err = cmd.Run(repo.Ctx, &RunOpts{ + if err = cmd.Run(repo.Ctx, &gitcmd.RunOpts{ Dir: repo.Path, Stdout: writer, Stderr: stderr, @@ -312,8 +313,8 @@ func GetAffectedFiles(repo *Repository, branchName, oldCommitID, newCommitID str affectedFiles := make([]string, 0, 32) // Run `git diff --name-only` to get the names of the changed files - err = NewCommand("diff", "--name-only").AddDynamicArguments(oldCommitID, newCommitID). - Run(repo.Ctx, &RunOpts{ + err = gitcmd.NewCommand("diff", "--name-only").AddDynamicArguments(oldCommitID, newCommitID). + Run(repo.Ctx, &gitcmd.RunOpts{ Env: env, Dir: repo.Path, Stdout: stdoutWriter, diff --git a/modules/git/git.go b/modules/git/git.go index 0a2e87a2eae..161fa42196a 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -9,12 +9,12 @@ import ( "errors" "fmt" "os" - "os/exec" "path/filepath" "runtime" "strings" "time" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -33,10 +33,7 @@ type Features struct { SupportCheckAttrOnBare bool // >= 2.40 } -var ( - GitExecutable = "git" // the command name of git, will be updated to an absolute path during initialization - defaultFeatures *Features -) +var defaultFeatures *Features func (f *Features) CheckVersionAtLeast(atLeast string) bool { return f.gitVersion.Compare(version.Must(version.NewVersion(atLeast))) >= 0 @@ -60,7 +57,7 @@ func DefaultFeatures() *Features { } func loadGitVersionFeatures() (*Features, error) { - stdout, _, runErr := NewCommand("version").RunStdString(context.Background(), nil) + stdout, _, runErr := gitcmd.NewCommand("version").RunStdString(context.Background(), nil) if runErr != nil { return nil, runErr } @@ -129,32 +126,6 @@ func ensureGitVersion() error { return nil } -// SetExecutablePath changes the path of git executable and checks the file permission and version. -func SetExecutablePath(path string) error { - // If path is empty, we use the default value of GitExecutable "git" to search for the location of git. - if path != "" { - GitExecutable = path - } - absPath, err := exec.LookPath(GitExecutable) - if err != nil { - return fmt.Errorf("git not found: %w", err) - } - GitExecutable = absPath - return nil -} - -// HomeDir is the home dir for git to store the global config file used by Gitea internally -func HomeDir() string { - if setting.Git.HomePath == "" { - // strict check, make sure the git module is initialized correctly. - // attention: when the git module is called in gitea sub-command (serv/hook), the log module might not obviously show messages to users/developers. - // for example: if there is gitea git hook code calling git.NewCommand before git.InitXxx, the integration test won't show the real failure reasons. - log.Fatal("Unable to init Git's HomeDir, incorrect initialization of the setting and git modules") - return "" - } - return setting.Git.HomePath -} - // InitSimple initializes git module with a very simple step, no config changes, no global command arguments. // This method doesn't change anything to filesystem. At the moment, it is only used by some Gitea sub-commands. func InitSimple() error { @@ -167,10 +138,10 @@ func InitSimple() error { } if setting.Git.Timeout.Default > 0 { - defaultCommandExecutionTimeout = time.Duration(setting.Git.Timeout.Default) * time.Second + gitcmd.SetDefaultCommandExecutionTimeout(time.Duration(setting.Git.Timeout.Default) * time.Second) } - if err := SetExecutablePath(setting.Git.Path); err != nil { + if err := gitcmd.SetExecutablePath(setting.Git.Path); err != nil { return err } @@ -185,7 +156,7 @@ func InitSimple() error { // when git works with gnupg (commit signing), there should be a stable home for gnupg commands if _, ok := os.LookupEnv("GNUPGHOME"); !ok { - _ = os.Setenv("GNUPGHOME", filepath.Join(HomeDir(), ".gnupg")) + _ = os.Setenv("GNUPGHOME", filepath.Join(gitcmd.HomeDir(), ".gnupg")) } return nil } diff --git a/modules/git/command.go b/modules/git/gitcmd/command.go similarity index 99% rename from modules/git/command.go rename to modules/git/gitcmd/command.go index eeb987feb0b..ed2f6fb647e 100644 --- a/modules/git/command.go +++ b/modules/git/gitcmd/command.go @@ -2,7 +2,7 @@ // Copyright 2016 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package git +package gitcmd import ( "bytes" @@ -32,6 +32,10 @@ type TrustedCmdArgs []internal.CmdArg // defaultCommandExecutionTimeout default command execution timeout duration var defaultCommandExecutionTimeout = 360 * time.Second +func SetDefaultCommandExecutionTimeout(timeout time.Duration) { + defaultCommandExecutionTimeout = timeout +} + // DefaultLocale is the default LC_ALL to run git commands in. const DefaultLocale = "C" diff --git a/modules/git/command_race_test.go b/modules/git/gitcmd/command_race_test.go similarity index 98% rename from modules/git/command_race_test.go rename to modules/git/gitcmd/command_race_test.go index a6aa3a1580a..aee2272808b 100644 --- a/modules/git/command_race_test.go +++ b/modules/git/gitcmd/command_race_test.go @@ -3,7 +3,7 @@ //go:build race -package git +package gitcmd import ( "context" diff --git a/modules/git/command_test.go b/modules/git/gitcmd/command_test.go similarity index 83% rename from modules/git/command_test.go rename to modules/git/gitcmd/command_test.go index 85976e289ef..544a97f64c1 100644 --- a/modules/git/command_test.go +++ b/modules/git/gitcmd/command_test.go @@ -1,14 +1,30 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package git +package gitcmd import ( + "fmt" + "os" "testing" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/tempdir" + "github.com/stretchr/testify/assert" ) +func TestMain(m *testing.M) { + gitHomePath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("git-home") + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "unable to create temp dir: %v", err) + os.Exit(1) + } + defer cleanup() + + setting.Git.HomePath = gitHomePath +} + func TestRunWithContextStd(t *testing.T) { cmd := NewCommand("--version") stdout, stderr, err := cmd.RunStdString(t.Context(), &RunOpts{}) diff --git a/modules/git/gitcmd/env.go b/modules/git/gitcmd/env.go new file mode 100644 index 00000000000..269b51a253a --- /dev/null +++ b/modules/git/gitcmd/env.go @@ -0,0 +1,40 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitcmd + +import ( + "fmt" + "os/exec" + + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" +) + +var GitExecutable = "git" // the command name of git, will be updated to an absolute path during initialization + +// SetExecutablePath changes the path of git executable and checks the file permission and version. +func SetExecutablePath(path string) error { + // If path is empty, we use the default value of GitExecutable "git" to search for the location of git. + if path != "" { + GitExecutable = path + } + absPath, err := exec.LookPath(GitExecutable) + if err != nil { + return fmt.Errorf("git not found: %w", err) + } + GitExecutable = absPath + return nil +} + +// HomeDir is the home dir for git to store the global config file used by Gitea internally +func HomeDir() string { + if setting.Git.HomePath == "" { + // strict check, make sure the git module is initialized correctly. + // attention: when the git module is called in gitea sub-command (serv/hook), the log module might not obviously show messages to users/developers. + // for example: if there is gitea git hook code calling NewCommand before git.InitXxx, the integration test won't show the real failure reasons. + log.Fatal("Unable to init Git's HomeDir, incorrect initialization of the setting and git modules") + return "" + } + return setting.Git.HomePath +} diff --git a/modules/git/gitcmd/utils.go b/modules/git/gitcmd/utils.go new file mode 100644 index 00000000000..ee24eb6a9a1 --- /dev/null +++ b/modules/git/gitcmd/utils.go @@ -0,0 +1,14 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitcmd + +import "fmt" + +// ConcatenateError concatenats an error with stderr string +func ConcatenateError(err error, stderr string) error { + if len(stderr) == 0 { + return err + } + return fmt.Errorf("%w - %s", err, stderr) +} diff --git a/modules/git/grep.go b/modules/git/grep.go index 66711650c96..f5f6f120416 100644 --- a/modules/git/grep.go +++ b/modules/git/grep.go @@ -14,6 +14,7 @@ import ( "strconv" "strings" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/util" ) @@ -60,7 +61,7 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO 2^@repo: go-gitea/gitea */ var results []*GrepResult - cmd := NewCommand("grep", "--null", "--break", "--heading", "--line-number", "--full-name") + cmd := gitcmd.NewCommand("grep", "--null", "--break", "--heading", "--line-number", "--full-name") cmd.AddOptionValues("--context", strconv.Itoa(opts.ContextLineNumber)) switch opts.GrepMode { case GrepModeExact: @@ -83,7 +84,7 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO cmd.AddDashesAndList(opts.PathspecList...) opts.MaxResultLimit = util.IfZero(opts.MaxResultLimit, 50) stderr := bytes.Buffer{} - err = cmd.Run(ctx, &RunOpts{ + err = cmd.Run(ctx, &gitcmd.RunOpts{ Dir: repo.Path, Stdout: stdoutWriter, Stderr: &stderr, @@ -135,11 +136,11 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO }, }) // git grep exits by cancel (killed), usually it is caused by the limit of results - if IsErrorExitCode(err, -1) && stderr.Len() == 0 { + if gitcmd.IsErrorExitCode(err, -1) && stderr.Len() == 0 { return results, nil } // git grep exits with 1 if no results are found - if IsErrorExitCode(err, 1) && stderr.Len() == 0 { + if gitcmd.IsErrorExitCode(err, 1) && stderr.Len() == 0 { return nil, nil } if err != nil && !errors.Is(err, context.Canceled) { diff --git a/modules/git/key.go b/modules/git/key.go index 2513c048b7c..8c14742f34a 100644 --- a/modules/git/key.go +++ b/modules/git/key.go @@ -3,13 +3,24 @@ package git +import "code.gitea.io/gitea/modules/setting" + // Based on https://git-scm.com/docs/git-config#Documentation/git-config.txt-gpgformat const ( SigningKeyFormatOpenPGP = "openpgp" // for GPG keys, the expected default of git cli SigningKeyFormatSSH = "ssh" ) +// SigningKey represents an instance key info which will be used to sign git commits. +// FIXME: need to refactor it to a new name, this name conflicts with the variable names for "asymkey.GPGKey" in many places. type SigningKey struct { KeyID string Format string } + +func (s *SigningKey) String() string { + // Do not expose KeyID + // In case the key is a file path and the struct is rendered in a template, then the server path will be exposed. + setting.PanicInDevOrTesting("don't call SigningKey.String() - it exposes the KeyID which might be a local file path") + return "SigningKey:" + s.Format +} diff --git a/modules/git/log_name_status.go b/modules/git/log_name_status.go index dfdef38ef9d..7a5192f58b7 100644 --- a/modules/git/log_name_status.go +++ b/modules/git/log_name_status.go @@ -14,6 +14,7 @@ import ( "strings" "code.gitea.io/gitea/modules/container" + "code.gitea.io/gitea/modules/git/gitcmd" "github.com/djherbis/buffer" "github.com/djherbis/nio/v3" @@ -34,7 +35,7 @@ func LogNameStatusRepo(ctx context.Context, repository, head, treepath string, p _ = stdoutWriter.Close() } - cmd := NewCommand() + cmd := gitcmd.NewCommand() cmd.AddArguments("log", "--name-status", "-c", "--format=commit%x00%H %P%x00", "--parents", "--no-renames", "-t", "-z").AddDynamicArguments(head) var files []string @@ -64,13 +65,13 @@ func LogNameStatusRepo(ctx context.Context, repository, head, treepath string, p go func() { stderr := strings.Builder{} - err := cmd.Run(ctx, &RunOpts{ + err := cmd.Run(ctx, &gitcmd.RunOpts{ Dir: repository, Stdout: stdoutWriter, Stderr: &stderr, }) if err != nil { - _ = stdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String())) + _ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String())) return } diff --git a/modules/git/pipeline/catfile.go b/modules/git/pipeline/catfile.go index 5ddc36cc01c..ced8532e6d9 100644 --- a/modules/git/pipeline/catfile.go +++ b/modules/git/pipeline/catfile.go @@ -13,7 +13,7 @@ import ( "strings" "sync" - "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" ) @@ -25,8 +25,8 @@ func CatFileBatchCheck(ctx context.Context, shasToCheckReader *io.PipeReader, ca stderr := new(bytes.Buffer) var errbuf strings.Builder - cmd := git.NewCommand("cat-file", "--batch-check") - if err := cmd.Run(ctx, &git.RunOpts{ + cmd := gitcmd.NewCommand("cat-file", "--batch-check") + if err := cmd.Run(ctx, &gitcmd.RunOpts{ Dir: tmpBasePath, Stdin: shasToCheckReader, Stdout: catFileCheckWriter, @@ -43,8 +43,8 @@ func CatFileBatchCheckAllObjects(ctx context.Context, catFileCheckWriter *io.Pip stderr := new(bytes.Buffer) var errbuf strings.Builder - cmd := git.NewCommand("cat-file", "--batch-check", "--batch-all-objects") - if err := cmd.Run(ctx, &git.RunOpts{ + cmd := gitcmd.NewCommand("cat-file", "--batch-check", "--batch-all-objects") + if err := cmd.Run(ctx, &gitcmd.RunOpts{ Dir: tmpBasePath, Stdout: catFileCheckWriter, Stderr: stderr, @@ -64,7 +64,7 @@ func CatFileBatch(ctx context.Context, shasToBatchReader *io.PipeReader, catFile stderr := new(bytes.Buffer) var errbuf strings.Builder - if err := git.NewCommand("cat-file", "--batch").Run(ctx, &git.RunOpts{ + if err := gitcmd.NewCommand("cat-file", "--batch").Run(ctx, &gitcmd.RunOpts{ Dir: tmpBasePath, Stdout: catFileBatchWriter, Stdin: shasToBatchReader, diff --git a/modules/git/pipeline/lfs_nogogit.go b/modules/git/pipeline/lfs_nogogit.go index c5eed737011..d2f147854d9 100644 --- a/modules/git/pipeline/lfs_nogogit.go +++ b/modules/git/pipeline/lfs_nogogit.go @@ -14,6 +14,7 @@ import ( "sync" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" ) // FindLFSFile finds commits that contain a provided pointer file hash @@ -32,13 +33,13 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err go func() { stderr := strings.Builder{} - err := git.NewCommand("rev-list", "--all").Run(repo.Ctx, &git.RunOpts{ + err := gitcmd.NewCommand("rev-list", "--all").Run(repo.Ctx, &gitcmd.RunOpts{ Dir: repo.Path, Stdout: revListWriter, Stderr: &stderr, }) if err != nil { - _ = revListWriter.CloseWithError(git.ConcatenateError(err, (&stderr).String())) + _ = revListWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String())) } else { _ = revListWriter.Close() } diff --git a/modules/git/pipeline/namerev.go b/modules/git/pipeline/namerev.go index 06731c50519..0081f7a26db 100644 --- a/modules/git/pipeline/namerev.go +++ b/modules/git/pipeline/namerev.go @@ -11,7 +11,7 @@ import ( "strings" "sync" - "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" ) // NameRevStdin runs name-rev --stdin @@ -22,7 +22,7 @@ func NameRevStdin(ctx context.Context, shasToNameReader *io.PipeReader, nameRevS stderr := new(bytes.Buffer) var errbuf strings.Builder - if err := git.NewCommand("name-rev", "--stdin", "--name-only", "--always").Run(ctx, &git.RunOpts{ + if err := gitcmd.NewCommand("name-rev", "--stdin", "--name-only", "--always").Run(ctx, &gitcmd.RunOpts{ Dir: tmpBasePath, Stdout: nameRevStdinWriter, Stdin: shasToNameReader, diff --git a/modules/git/pipeline/revlist.go b/modules/git/pipeline/revlist.go index 31627a0f3a7..9d4ff754341 100644 --- a/modules/git/pipeline/revlist.go +++ b/modules/git/pipeline/revlist.go @@ -12,7 +12,7 @@ import ( "strings" "sync" - "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" ) @@ -23,8 +23,8 @@ func RevListAllObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sy stderr := new(bytes.Buffer) var errbuf strings.Builder - cmd := git.NewCommand("rev-list", "--objects", "--all") - if err := cmd.Run(ctx, &git.RunOpts{ + cmd := gitcmd.NewCommand("rev-list", "--objects", "--all") + if err := cmd.Run(ctx, &gitcmd.RunOpts{ Dir: basePath, Stdout: revListWriter, Stderr: stderr, @@ -42,11 +42,11 @@ func RevListObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sync. defer revListWriter.Close() stderr := new(bytes.Buffer) var errbuf strings.Builder - cmd := git.NewCommand("rev-list", "--objects").AddDynamicArguments(headSHA) + cmd := gitcmd.NewCommand("rev-list", "--objects").AddDynamicArguments(headSHA) if baseSHA != "" { cmd = cmd.AddArguments("--not").AddDynamicArguments(baseSHA) } - if err := cmd.Run(ctx, &git.RunOpts{ + if err := cmd.Run(ctx, &gitcmd.RunOpts{ Dir: tmpBasePath, Stdout: revListWriter, Stderr: stderr, diff --git a/modules/git/remote.go b/modules/git/remote.go index 283d26484bb..9f12142f916 100644 --- a/modules/git/remote.go +++ b/modules/git/remote.go @@ -9,19 +9,20 @@ import ( "net/url" "strings" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/util" ) // GetRemoteAddress returns remote url of git repository in the repoPath with special remote name func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string, error) { - var cmd *Command + var cmd *gitcmd.Command if DefaultFeatures().CheckVersionAtLeast("2.7") { - cmd = NewCommand("remote", "get-url").AddDynamicArguments(remoteName) + cmd = gitcmd.NewCommand("remote", "get-url").AddDynamicArguments(remoteName) } else { - cmd = NewCommand("config", "--get").AddDynamicArguments("remote." + remoteName + ".url") + cmd = gitcmd.NewCommand("config", "--get").AddDynamicArguments("remote." + remoteName + ".url") } - result, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath}) + result, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if err != nil { return "", err } diff --git a/modules/git/repo.go b/modules/git/repo.go index 8e7b427bd0e..38cb4592a04 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -17,6 +17,7 @@ import ( "strings" "time" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/proxy" "code.gitea.io/gitea/modules/setting" ) @@ -40,9 +41,9 @@ func (repo *Repository) GetAllCommitsCount() (int64, error) { func (repo *Repository) ShowPrettyFormatLogToList(ctx context.Context, revisionRange string) ([]*Commit, error) { // avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git [...] -- [...]' - logs, _, err := NewCommand("log").AddArguments(prettyLogFormat). + logs, _, err := gitcmd.NewCommand("log").AddArguments(prettyLogFormat). AddDynamicArguments(revisionRange).AddArguments("--"). - RunStdBytes(ctx, &RunOpts{Dir: repo.Path}) + RunStdBytes(ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil { return nil, err } @@ -70,7 +71,7 @@ func (repo *Repository) parsePrettyFormatLogToList(logs []byte) ([]*Commit, erro // IsRepoURLAccessible checks if given repository URL is accessible. func IsRepoURLAccessible(ctx context.Context, url string) bool { - _, _, err := NewCommand("ls-remote", "-q", "-h").AddDynamicArguments(url, "HEAD").RunStdString(ctx, nil) + _, _, err := gitcmd.NewCommand("ls-remote", "-q", "-h").AddDynamicArguments(url, "HEAD").RunStdString(ctx, nil) return err == nil } @@ -81,7 +82,7 @@ func InitRepository(ctx context.Context, repoPath string, bare bool, objectForma return err } - cmd := NewCommand("init") + cmd := gitcmd.NewCommand("init") if !IsValidObjectFormat(objectFormatName) { return fmt.Errorf("invalid object format: %s", objectFormatName) @@ -93,15 +94,15 @@ func InitRepository(ctx context.Context, repoPath string, bare bool, objectForma if bare { cmd.AddArguments("--bare") } - _, _, err = cmd.RunStdString(ctx, &RunOpts{Dir: repoPath}) + _, _, err = cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) return err } // IsEmpty Check if repository is empty. func (repo *Repository) IsEmpty() (bool, error) { var errbuf, output strings.Builder - if err := NewCommand().AddOptionFormat("--git-dir=%s", repo.Path).AddArguments("rev-list", "-n", "1", "--all"). - Run(repo.Ctx, &RunOpts{ + if err := gitcmd.NewCommand().AddOptionFormat("--git-dir=%s", repo.Path).AddArguments("rev-list", "-n", "1", "--all"). + Run(repo.Ctx, &gitcmd.RunOpts{ Dir: repo.Path, Stdout: &output, Stderr: &errbuf, @@ -137,7 +138,7 @@ func Clone(ctx context.Context, from, to string, opts CloneRepoOptions) error { return err } - cmd := NewCommand().AddArguments("clone") + cmd := gitcmd.NewCommand().AddArguments("clone") if opts.SkipTLSVerify { cmd.AddArguments("-c", "http.sslVerify=false") } @@ -178,13 +179,13 @@ func Clone(ctx context.Context, from, to string, opts CloneRepoOptions) error { } stderr := new(bytes.Buffer) - if err = cmd.Run(ctx, &RunOpts{ + if err = cmd.Run(ctx, &gitcmd.RunOpts{ Timeout: opts.Timeout, Env: envs, Stdout: io.Discard, Stderr: stderr, }); err != nil { - return ConcatenateError(err, stderr.String()) + return gitcmd.ConcatenateError(err, stderr.String()) } return nil } @@ -201,7 +202,7 @@ type PushOptions struct { // Push pushs local commits to given remote branch. func Push(ctx context.Context, repoPath string, opts PushOptions) error { - cmd := NewCommand("push") + cmd := gitcmd.NewCommand("push") if opts.Force { cmd.AddArguments("-f") } @@ -214,7 +215,7 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error { } cmd.AddDashesAndList(remoteBranchArgs...) - stdout, stderr, err := cmd.RunStdString(ctx, &RunOpts{Env: opts.Env, Timeout: opts.Timeout, Dir: repoPath}) + stdout, stderr, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Env: opts.Env, Timeout: opts.Timeout, Dir: repoPath}) if err != nil { if strings.Contains(stderr, "non-fast-forward") { return &ErrPushOutOfDate{StdOut: stdout, StdErr: stderr, Err: err} @@ -233,8 +234,8 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error { // GetLatestCommitTime returns time for latest commit in repository (across all branches) func GetLatestCommitTime(ctx context.Context, repoPath string) (time.Time, error) { - cmd := NewCommand("for-each-ref", "--sort=-committerdate", BranchPrefix, "--count", "1", "--format=%(committerdate)") - stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath}) + cmd := gitcmd.NewCommand("for-each-ref", "--sort=-committerdate", BranchPrefix, "--count", "1", "--format=%(committerdate)") + stdout, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if err != nil { return time.Time{}, err } @@ -250,9 +251,9 @@ type DivergeObject struct { // GetDivergingCommits returns the number of commits a targetBranch is ahead or behind a baseBranch func GetDivergingCommits(ctx context.Context, repoPath, baseBranch, targetBranch string) (do DivergeObject, err error) { - cmd := NewCommand("rev-list", "--count", "--left-right"). + cmd := gitcmd.NewCommand("rev-list", "--count", "--left-right"). AddDynamicArguments(baseBranch + "..." + targetBranch).AddArguments("--") - stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath}) + stdout, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if err != nil { return do, err } @@ -281,23 +282,23 @@ func (repo *Repository) CreateBundle(ctx context.Context, commit string, out io. defer cleanup() env := append(os.Environ(), "GIT_OBJECT_DIRECTORY="+filepath.Join(repo.Path, "objects")) - _, _, err = NewCommand("init", "--bare").RunStdString(ctx, &RunOpts{Dir: tmp, Env: env}) + _, _, err = gitcmd.NewCommand("init", "--bare").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env}) if err != nil { return err } - _, _, err = NewCommand("reset", "--soft").AddDynamicArguments(commit).RunStdString(ctx, &RunOpts{Dir: tmp, Env: env}) + _, _, err = gitcmd.NewCommand("reset", "--soft").AddDynamicArguments(commit).RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env}) if err != nil { return err } - _, _, err = NewCommand("branch", "-m", "bundle").RunStdString(ctx, &RunOpts{Dir: tmp, Env: env}) + _, _, err = gitcmd.NewCommand("branch", "-m", "bundle").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env}) if err != nil { return err } tmpFile := filepath.Join(tmp, "bundle") - _, _, err = NewCommand("bundle", "create").AddDynamicArguments(tmpFile, "bundle", "HEAD").RunStdString(ctx, &RunOpts{Dir: tmp, Env: env}) + _, _, err = gitcmd.NewCommand("bundle", "create").AddDynamicArguments(tmpFile, "bundle", "HEAD").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmp, Env: env}) if err != nil { return err } diff --git a/modules/git/repo_archive.go b/modules/git/repo_archive.go index 0b2f6f2a453..e12300345f1 100644 --- a/modules/git/repo_archive.go +++ b/modules/git/repo_archive.go @@ -10,6 +10,8 @@ import ( "io" "path/filepath" "strings" + + "code.gitea.io/gitea/modules/git/gitcmd" ) // ArchiveType archive types @@ -53,7 +55,7 @@ func (repo *Repository) CreateArchive(ctx context.Context, format ArchiveType, t return fmt.Errorf("unknown format: %v", format) } - cmd := NewCommand("archive") + cmd := gitcmd.NewCommand("archive") if usePrefix { cmd.AddOptionFormat("--prefix=%s", filepath.Base(strings.TrimSuffix(repo.Path, ".git"))+"/") } @@ -61,13 +63,13 @@ func (repo *Repository) CreateArchive(ctx context.Context, format ArchiveType, t cmd.AddDynamicArguments(commitID) var stderr strings.Builder - err := cmd.Run(ctx, &RunOpts{ + err := cmd.Run(ctx, &gitcmd.RunOpts{ Dir: repo.Path, Stdout: target, Stderr: &stderr, }) if err != nil { - return ConcatenateError(err, stderr.String()) + return gitcmd.ConcatenateError(err, stderr.String()) } return nil } diff --git a/modules/git/repo_blame.go b/modules/git/repo_blame.go index 6941a76c42d..04f74049aab 100644 --- a/modules/git/repo_blame.go +++ b/modules/git/repo_blame.go @@ -5,14 +5,16 @@ package git import ( "fmt" + + "code.gitea.io/gitea/modules/git/gitcmd" ) // LineBlame returns the latest commit at the given line func (repo *Repository) LineBlame(revision, path, file string, line uint) (*Commit, error) { - res, _, err := NewCommand("blame"). + res, _, err := gitcmd.NewCommand("blame"). AddOptionFormat("-L %d,%d", line, line). AddOptionValues("-p", revision). - AddDashesAndList(file).RunStdString(repo.Ctx, &RunOpts{Dir: path}) + AddDashesAndList(file).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: path}) if err != nil { return nil, err } diff --git a/modules/git/repo_branch.go b/modules/git/repo_branch.go index eac20310035..5d4c05bb28a 100644 --- a/modules/git/repo_branch.go +++ b/modules/git/repo_branch.go @@ -8,6 +8,8 @@ import ( "context" "errors" "strings" + + "code.gitea.io/gitea/modules/git/gitcmd" ) // BranchPrefix base dir of the branch information file store on git @@ -15,7 +17,7 @@ const BranchPrefix = "refs/heads/" // IsReferenceExist returns true if given reference exists in the repository. func IsReferenceExist(ctx context.Context, repoPath, name string) bool { - _, _, err := NewCommand("show-ref", "--verify").AddDashesAndList(name).RunStdString(ctx, &RunOpts{Dir: repoPath}) + _, _, err := gitcmd.NewCommand("show-ref", "--verify").AddDashesAndList(name).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) return err == nil } @@ -25,7 +27,7 @@ func IsBranchExist(ctx context.Context, repoPath, name string) bool { } func GetDefaultBranch(ctx context.Context, repoPath string) (string, error) { - stdout, _, err := NewCommand("symbolic-ref", "HEAD").RunStdString(ctx, &RunOpts{Dir: repoPath}) + stdout, _, err := gitcmd.NewCommand("symbolic-ref", "HEAD").RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if err != nil { return "", err } @@ -43,7 +45,7 @@ type DeleteBranchOptions struct { // DeleteBranch delete a branch by name on repository. func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) error { - cmd := NewCommand("branch") + cmd := gitcmd.NewCommand("branch") if opts.Force { cmd.AddArguments("-D") @@ -52,35 +54,35 @@ func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) erro } cmd.AddDashesAndList(name) - _, _, err := cmd.RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + _, _, err := cmd.RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) return err } // CreateBranch create a new branch func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error { - cmd := NewCommand("branch") + cmd := gitcmd.NewCommand("branch") cmd.AddDashesAndList(branch, oldbranchOrCommit) - _, _, err := cmd.RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + _, _, err := cmd.RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) return err } // AddRemote adds a new remote to repository. func (repo *Repository) AddRemote(name, url string, fetch bool) error { - cmd := NewCommand("remote", "add") + cmd := gitcmd.NewCommand("remote", "add") if fetch { cmd.AddArguments("-f") } cmd.AddDynamicArguments(name, url) - _, _, err := cmd.RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + _, _, err := cmd.RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) return err } // RenameBranch rename a branch func (repo *Repository) RenameBranch(from, to string) error { - _, _, err := NewCommand("branch", "-m").AddDynamicArguments(from, to).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + _, _, err := gitcmd.NewCommand("branch", "-m").AddDynamicArguments(from, to).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) return err } diff --git a/modules/git/repo_branch_nogogit.go b/modules/git/repo_branch_nogogit.go index 0d111985235..255c2974e9b 100644 --- a/modules/git/repo_branch_nogogit.go +++ b/modules/git/repo_branch_nogogit.go @@ -13,6 +13,7 @@ import ( "io" "strings" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" ) @@ -70,25 +71,25 @@ func (repo *Repository) IsBranchExist(name string) bool { // GetBranchNames returns branches from the repository, skipping "skip" initial branches and // returning at most "limit" branches, or all branches if "limit" is 0. func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) { - return callShowRef(repo.Ctx, repo.Path, BranchPrefix, TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"}, skip, limit) + return callShowRef(repo.Ctx, repo.Path, BranchPrefix, gitcmd.TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"}, skip, limit) } // WalkReferences walks all the references from the repository // refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty. func (repo *Repository) WalkReferences(refType ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) { - var args TrustedCmdArgs + var args gitcmd.TrustedCmdArgs switch refType { case ObjectTag: - args = TrustedCmdArgs{TagPrefix, "--sort=-taggerdate"} + args = gitcmd.TrustedCmdArgs{TagPrefix, "--sort=-taggerdate"} case ObjectBranch: - args = TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"} + args = gitcmd.TrustedCmdArgs{BranchPrefix, "--sort=-committerdate"} } return WalkShowRef(repo.Ctx, repo.Path, args, skip, limit, walkfn) } // callShowRef return refs, if limit = 0 it will not limit -func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs TrustedCmdArgs, skip, limit int) (branchNames []string, countAll int, err error) { +func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs gitcmd.TrustedCmdArgs, skip, limit int) (branchNames []string, countAll int, err error) { countAll, err = WalkShowRef(ctx, repoPath, extraArgs, skip, limit, func(_, branchName string) error { branchName = strings.TrimPrefix(branchName, trimPrefix) branchNames = append(branchNames, branchName) @@ -98,7 +99,7 @@ func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs Tru return branchNames, countAll, err } -func WalkShowRef(ctx context.Context, repoPath string, extraArgs TrustedCmdArgs, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) { +func WalkShowRef(ctx context.Context, repoPath string, extraArgs gitcmd.TrustedCmdArgs, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) { stdoutReader, stdoutWriter := io.Pipe() defer func() { _ = stdoutReader.Close() @@ -107,9 +108,9 @@ func WalkShowRef(ctx context.Context, repoPath string, extraArgs TrustedCmdArgs, go func() { stderrBuilder := &strings.Builder{} - args := TrustedCmdArgs{"for-each-ref", "--format=%(objectname) %(refname)"} + args := gitcmd.TrustedCmdArgs{"for-each-ref", "--format=%(objectname) %(refname)"} args = append(args, extraArgs...) - err := NewCommand(args...).Run(ctx, &RunOpts{ + err := gitcmd.NewCommand(args...).Run(ctx, &gitcmd.RunOpts{ Dir: repoPath, Stdout: stdoutWriter, Stderr: stderrBuilder, @@ -119,7 +120,7 @@ func WalkShowRef(ctx context.Context, repoPath string, extraArgs TrustedCmdArgs, _ = stdoutWriter.Close() return } - _ = stdoutWriter.CloseWithError(ConcatenateError(err, stderrBuilder.String())) + _ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, stderrBuilder.String())) } else { _ = stdoutWriter.Close() } diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index 4066a1ca7ba..6e5911f1ddd 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -12,6 +12,7 @@ import ( "strings" "code.gitea.io/gitea/modules/cache" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/setting" ) @@ -59,7 +60,7 @@ func (repo *Repository) getCommitByPathWithID(id ObjectID, relpath string) (*Com relpath = `\` + relpath } - stdout, _, runErr := NewCommand("log", "-1", prettyLogFormat).AddDynamicArguments(id.String()).AddDashesAndList(relpath).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, runErr := gitcmd.NewCommand("log", "-1", prettyLogFormat).AddDynamicArguments(id.String()).AddDashesAndList(relpath).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if runErr != nil { return nil, runErr } @@ -74,7 +75,7 @@ func (repo *Repository) getCommitByPathWithID(id ObjectID, relpath string) (*Com // GetCommitByPath returns the last commit of relative path. func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) { - stdout, _, runErr := NewCommand("log", "-1", prettyLogFormat).AddDashesAndList(relpath).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, runErr := gitcmd.NewCommand("log", "-1", prettyLogFormat).AddDashesAndList(relpath).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if runErr != nil { return nil, runErr } @@ -91,7 +92,7 @@ func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) { // commitsByRangeWithTime returns the specific page commits before current revision, with not, since, until support func (repo *Repository) commitsByRangeWithTime(id ObjectID, page, pageSize int, not, since, until string) ([]*Commit, error) { - cmd := NewCommand("log"). + cmd := gitcmd.NewCommand("log"). AddOptionFormat("--skip=%d", (page-1)*pageSize). AddOptionFormat("--max-count=%d", pageSize). AddArguments(prettyLogFormat). @@ -107,7 +108,7 @@ func (repo *Repository) commitsByRangeWithTime(id ObjectID, page, pageSize int, cmd.AddOptionFormat("--until=%s", until) } - stdout, _, err := cmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil { return nil, err } @@ -117,7 +118,7 @@ func (repo *Repository) commitsByRangeWithTime(id ObjectID, page, pageSize int, func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([]*Commit, error) { // add common arguments to git command - addCommonSearchArgs := func(c *Command) { + addCommonSearchArgs := func(c *gitcmd.Command) { // ignore case c.AddArguments("-i") @@ -141,7 +142,7 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([ } // create new git log command with limit of 100 commits - cmd := NewCommand("log", "-100", prettyLogFormat).AddDynamicArguments(id.String()) + cmd := gitcmd.NewCommand("log", "-100", prettyLogFormat).AddDynamicArguments(id.String()) // pretend that all refs along with HEAD were listed on command line as // https://git-scm.com/docs/git-log#Documentation/git-log.txt---all @@ -161,7 +162,7 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([ // search for commits matching given constraints and keywords in commit msg addCommonSearchArgs(cmd) - stdout, _, err := cmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil { return nil, err } @@ -175,14 +176,14 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([ // ignore anything not matching a valid sha pattern if id.Type().IsValid(v) { // create new git log command with 1 commit limit - hashCmd := NewCommand("log", "-1", prettyLogFormat) + hashCmd := gitcmd.NewCommand("log", "-1", prettyLogFormat) // add previous arguments except for --grep and --all addCommonSearchArgs(hashCmd) // add keyword as hashCmd.AddDynamicArguments(v) // search with given constraints for commit matching sha hash of v - hashMatching, _, err := hashCmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + hashMatching, _, err := hashCmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil || bytes.Contains(stdout, hashMatching) { continue } @@ -197,7 +198,7 @@ func (repo *Repository) searchCommits(id ObjectID, opts SearchCommitsOptions) ([ // FileChangedBetweenCommits Returns true if the file changed between commit IDs id1 and id2 // You must ensure that id1 and id2 are valid commit ids. func (repo *Repository) FileChangedBetweenCommits(filename, id1, id2 string) (bool, error) { - stdout, _, err := NewCommand("diff", "--name-only", "-z").AddDynamicArguments(id1, id2).AddDashesAndList(filename).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err := gitcmd.NewCommand("diff", "--name-only", "-z").AddDynamicArguments(id1, id2).AddDashesAndList(filename).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil { return false, err } @@ -232,7 +233,7 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions) }() go func() { stderr := strings.Builder{} - gitCmd := NewCommand("rev-list"). + gitCmd := gitcmd.NewCommand("rev-list"). AddOptionFormat("--max-count=%d", setting.Git.CommitsRangeSize). AddOptionFormat("--skip=%d", (opts.Page-1)*setting.Git.CommitsRangeSize) gitCmd.AddDynamicArguments(opts.Revision) @@ -248,13 +249,13 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions) } gitCmd.AddDashesAndList(opts.File) - err := gitCmd.Run(repo.Ctx, &RunOpts{ + err := gitCmd.Run(repo.Ctx, &gitcmd.RunOpts{ Dir: repo.Path, Stdout: stdoutWriter, Stderr: &stderr, }) if err != nil { - _ = stdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String())) + _ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, (&stderr).String())) } else { _ = stdoutWriter.Close() } @@ -290,11 +291,11 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions) // FilesCountBetween return the number of files changed between two commits func (repo *Repository) FilesCountBetween(startCommitID, endCommitID string) (int, error) { - stdout, _, err := NewCommand("diff", "--name-only").AddDynamicArguments(startCommitID+"..."+endCommitID).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err := gitcmd.NewCommand("diff", "--name-only").AddDynamicArguments(startCommitID+"..."+endCommitID).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil && strings.Contains(err.Error(), "no merge base") { // git >= 2.28 now returns an error if startCommitID and endCommitID have become unrelated. // previously it would return the results of git diff --name-only startCommitID endCommitID so let's try that... - stdout, _, err = NewCommand("diff", "--name-only").AddDynamicArguments(startCommitID, endCommitID).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err = gitcmd.NewCommand("diff", "--name-only").AddDynamicArguments(startCommitID, endCommitID).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) } if err != nil { return 0, err @@ -308,13 +309,13 @@ func (repo *Repository) CommitsBetween(last, before *Commit) ([]*Commit, error) var stdout []byte var err error if before == nil { - stdout, _, err = NewCommand("rev-list").AddDynamicArguments(last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) } else { - stdout, _, err = NewCommand("rev-list").AddDynamicArguments(before.ID.String()+".."+last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(before.ID.String()+".."+last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil && strings.Contains(err.Error(), "no merge base") { // future versions of git >= 2.28 are likely to return an error if before and last have become unrelated. // previously it would return the results of git rev-list before last so let's try that... - stdout, _, err = NewCommand("rev-list").AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) } } if err != nil { @@ -328,22 +329,22 @@ func (repo *Repository) CommitsBetweenLimit(last, before *Commit, limit, skip in var stdout []byte var err error if before == nil { - stdout, _, err = NewCommand("rev-list"). + stdout, _, err = gitcmd.NewCommand("rev-list"). AddOptionValues("--max-count", strconv.Itoa(limit)). AddOptionValues("--skip", strconv.Itoa(skip)). - AddDynamicArguments(last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + AddDynamicArguments(last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) } else { - stdout, _, err = NewCommand("rev-list"). + stdout, _, err = gitcmd.NewCommand("rev-list"). AddOptionValues("--max-count", strconv.Itoa(limit)). AddOptionValues("--skip", strconv.Itoa(skip)). - AddDynamicArguments(before.ID.String()+".."+last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + AddDynamicArguments(before.ID.String()+".."+last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil && strings.Contains(err.Error(), "no merge base") { // future versions of git >= 2.28 are likely to return an error if before and last have become unrelated. // previously it would return the results of git rev-list --max-count n before last so let's try that... - stdout, _, err = NewCommand("rev-list"). + stdout, _, err = gitcmd.NewCommand("rev-list"). AddOptionValues("--max-count", strconv.Itoa(limit)). AddOptionValues("--skip", strconv.Itoa(skip)). - AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) } } if err != nil { @@ -358,13 +359,13 @@ func (repo *Repository) CommitsBetweenNotBase(last, before *Commit, baseBranch s var stdout []byte var err error if before == nil { - stdout, _, err = NewCommand("rev-list").AddDynamicArguments(last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) } else { - stdout, _, err = NewCommand("rev-list").AddDynamicArguments(before.ID.String()+".."+last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(before.ID.String()+".."+last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil && strings.Contains(err.Error(), "no merge base") { // future versions of git >= 2.28 are likely to return an error if before and last have become unrelated. // previously it would return the results of git rev-list before last so let's try that... - stdout, _, err = NewCommand("rev-list").AddDynamicArguments(before.ID.String(), last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err = gitcmd.NewCommand("rev-list").AddDynamicArguments(before.ID.String(), last.ID.String()).AddOptionValues("--not", baseBranch).RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) } } if err != nil { @@ -410,13 +411,13 @@ func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) { // commitsBefore the limit is depth, not total number of returned commits. func (repo *Repository) commitsBefore(id ObjectID, limit int) ([]*Commit, error) { - cmd := NewCommand("log", prettyLogFormat) + cmd := gitcmd.NewCommand("log", prettyLogFormat) if limit > 0 { cmd.AddOptionFormat("-%d", limit) } cmd.AddDynamicArguments(id.String()) - stdout, _, runErr := cmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, runErr := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if runErr != nil { return nil, runErr } @@ -453,10 +454,10 @@ func (repo *Repository) getCommitsBeforeLimit(id ObjectID, num int) ([]*Commit, func (repo *Repository) getBranches(env []string, commitID string, limit int) ([]string, error) { if DefaultFeatures().CheckVersionAtLeast("2.7.0") { - stdout, _, err := NewCommand("for-each-ref", "--format=%(refname:strip=2)"). + stdout, _, err := gitcmd.NewCommand("for-each-ref", "--format=%(refname:strip=2)"). AddOptionFormat("--count=%d", limit). AddOptionValues("--contains", commitID, BranchPrefix). - RunStdString(repo.Ctx, &RunOpts{ + RunStdString(repo.Ctx, &gitcmd.RunOpts{ Dir: repo.Path, Env: env, }) @@ -468,7 +469,7 @@ func (repo *Repository) getBranches(env []string, commitID string, limit int) ([ return branches, nil } - stdout, _, err := NewCommand("branch").AddOptionValues("--contains", commitID).RunStdString(repo.Ctx, &RunOpts{ + stdout, _, err := gitcmd.NewCommand("branch").AddOptionValues("--contains", commitID).RunStdString(repo.Ctx, &gitcmd.RunOpts{ Dir: repo.Path, Env: env, }) @@ -510,7 +511,7 @@ func (repo *Repository) GetCommitsFromIDs(commitIDs []string) []*Commit { // IsCommitInBranch check if the commit is on the branch func (repo *Repository) IsCommitInBranch(commitID, branch string) (r bool, err error) { - stdout, _, err := NewCommand("branch", "--contains").AddDynamicArguments(commitID, branch).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err := gitcmd.NewCommand("branch", "--contains").AddDynamicArguments(commitID, branch).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil { return false, err } @@ -536,10 +537,10 @@ func (repo *Repository) AddLastCommitCache(cacheKey, fullName, sha string) error // GetCommitBranchStart returns the commit where the branch diverged func (repo *Repository) GetCommitBranchStart(env []string, branch, endCommitID string) (string, error) { - cmd := NewCommand("log", prettyLogFormat) + cmd := gitcmd.NewCommand("log", prettyLogFormat) cmd.AddDynamicArguments(endCommitID) - stdout, _, runErr := cmd.RunStdBytes(repo.Ctx, &RunOpts{ + stdout, _, runErr := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{ Dir: repo.Path, Env: env, }) diff --git a/modules/git/repo_commit_gogit.go b/modules/git/repo_commit_gogit.go index a88902e209d..fc653714adb 100644 --- a/modules/git/repo_commit_gogit.go +++ b/modules/git/repo_commit_gogit.go @@ -9,6 +9,8 @@ package git import ( "strings" + "code.gitea.io/gitea/modules/git/gitcmd" + "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/hash" "github.com/go-git/go-git/v5/plumbing/object" @@ -36,16 +38,6 @@ func (repo *Repository) GetRefCommitID(name string) (string, error) { return ref.Hash().String(), nil } -// SetReference sets the commit ID string of given reference (e.g. branch or tag). -func (repo *Repository) SetReference(name, commitID string) error { - return repo.gogitRepo.Storer.SetReference(plumbing.NewReferenceFromStrings(name, commitID)) -} - -// RemoveReference removes the given reference (e.g. branch or tag). -func (repo *Repository) RemoveReference(name string) error { - return repo.gogitRepo.Storer.RemoveReference(plumbing.ReferenceName(name)) -} - // ConvertToHash returns a Hash object from a potential ID string func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) { objectFormat, err := repo.GetObjectFormat() @@ -59,7 +51,7 @@ func (repo *Repository) ConvertToGitID(commitID string) (ObjectID, error) { } } - actualCommitID, _, err := NewCommand("rev-parse", "--verify").AddDynamicArguments(commitID).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + actualCommitID, _, err := gitcmd.NewCommand("rev-parse", "--verify").AddDynamicArguments(commitID).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) actualCommitID = strings.TrimSpace(actualCommitID) if err != nil { if strings.Contains(err.Error(), "unknown revision or path") || diff --git a/modules/git/repo_commit_nogogit.go b/modules/git/repo_commit_nogogit.go index 3ead3e22165..d2c66a541b7 100644 --- a/modules/git/repo_commit_nogogit.go +++ b/modules/git/repo_commit_nogogit.go @@ -11,12 +11,13 @@ import ( "io" "strings" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" ) // ResolveReference resolves a name to a reference func (repo *Repository) ResolveReference(name string) (string, error) { - stdout, _, err := NewCommand("show-ref", "--hash").AddDynamicArguments(name).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err := gitcmd.NewCommand("show-ref", "--hash").AddDynamicArguments(name).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil { if strings.Contains(err.Error(), "not a valid ref") { return "", ErrNotExist{name, ""} @@ -50,25 +51,13 @@ func (repo *Repository) GetRefCommitID(name string) (string, error) { return string(shaBs), nil } -// SetReference sets the commit ID string of given reference (e.g. branch or tag). -func (repo *Repository) SetReference(name, commitID string) error { - _, _, err := NewCommand("update-ref").AddDynamicArguments(name, commitID).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) - return err -} - -// RemoveReference removes the given reference (e.g. branch or tag). -func (repo *Repository) RemoveReference(name string) error { - _, _, err := NewCommand("update-ref", "--no-deref", "-d").AddDynamicArguments(name).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) - return err -} - // IsCommitExist returns true if given commit exists in current repository. func (repo *Repository) IsCommitExist(name string) bool { if err := ensureValidGitRepository(repo.Ctx, repo.Path); err != nil { log.Error("IsCommitExist: %v", err) return false } - _, _, err := NewCommand("cat-file", "-e").AddDynamicArguments(name).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + _, _, err := gitcmd.NewCommand("cat-file", "-e").AddDynamicArguments(name).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) return err == nil } diff --git a/modules/git/repo_commitgraph.go b/modules/git/repo_commitgraph.go index 62c63780549..331c799b334 100644 --- a/modules/git/repo_commitgraph.go +++ b/modules/git/repo_commitgraph.go @@ -6,13 +6,15 @@ package git import ( "context" "fmt" + + "code.gitea.io/gitea/modules/git/gitcmd" ) // WriteCommitGraph write commit graph to speed up repo access // this requires git v2.18 to be installed func WriteCommitGraph(ctx context.Context, repoPath string) error { if DefaultFeatures().CheckVersionAtLeast("2.18") { - if _, _, err := NewCommand("commit-graph", "write").RunStdString(ctx, &RunOpts{Dir: repoPath}); err != nil { + if _, _, err := gitcmd.NewCommand("commit-graph", "write").RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}); err != nil { return fmt.Errorf("unable to write commit-graph for '%s' : %w", repoPath, err) } } diff --git a/modules/git/repo_compare.go b/modules/git/repo_compare.go index 56251a87a0f..69a7314d5cf 100644 --- a/modules/git/repo_compare.go +++ b/modules/git/repo_compare.go @@ -16,6 +16,8 @@ import ( "regexp" "strconv" "strings" + + "code.gitea.io/gitea/modules/git/gitcmd" ) // GetMergeBase checks and returns merge base of two branches and the reference used as base. @@ -27,13 +29,13 @@ func (repo *Repository) GetMergeBase(tmpRemote, base, head string) (string, stri if tmpRemote != "origin" { tmpBaseName := RemotePrefix + tmpRemote + "/tmp_" + base // Fetch commit into a temporary branch in order to be able to handle commits and tags - _, _, err := NewCommand("fetch", "--no-tags").AddDynamicArguments(tmpRemote).AddDashesAndList(base+":"+tmpBaseName).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + _, _, err := gitcmd.NewCommand("fetch", "--no-tags").AddDynamicArguments(tmpRemote).AddDashesAndList(base+":"+tmpBaseName).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err == nil { base = tmpBaseName } } - stdout, _, err := NewCommand("merge-base").AddDashesAndList(base, head).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err := gitcmd.NewCommand("merge-base").AddDashesAndList(base, head).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) return strings.TrimSpace(stdout), base, err } @@ -61,8 +63,8 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis } // avoid: ambiguous argument 'refs/a...refs/b': unknown revision or path not in the working tree. Use '--': 'git [...] -- [...]' - if err := NewCommand("diff", "-z", "--name-only").AddDynamicArguments(base+separator+head).AddArguments("--"). - Run(repo.Ctx, &RunOpts{ + if err := gitcmd.NewCommand("diff", "-z", "--name-only").AddDynamicArguments(base+separator+head).AddArguments("--"). + Run(repo.Ctx, &gitcmd.RunOpts{ Dir: repo.Path, Stdout: w, Stderr: stderr, @@ -72,7 +74,7 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis // previously it would return the results of git diff -z --name-only base head so let's try that... w = &lineCountWriter{} stderr.Reset() - if err = NewCommand("diff", "-z", "--name-only").AddDynamicArguments(base, head).AddArguments("--").Run(repo.Ctx, &RunOpts{ + if err = gitcmd.NewCommand("diff", "-z", "--name-only").AddDynamicArguments(base, head).AddArguments("--").Run(repo.Ctx, &gitcmd.RunOpts{ Dir: repo.Path, Stdout: w, Stderr: stderr, @@ -87,13 +89,13 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis // GetDiffShortStatByCmdArgs counts number of changed files, number of additions and deletions // TODO: it can be merged with another "GetDiffShortStat" in the future -func GetDiffShortStatByCmdArgs(ctx context.Context, repoPath string, trustedArgs TrustedCmdArgs, dynamicArgs ...string) (numFiles, totalAdditions, totalDeletions int, err error) { +func GetDiffShortStatByCmdArgs(ctx context.Context, repoPath string, trustedArgs gitcmd.TrustedCmdArgs, dynamicArgs ...string) (numFiles, totalAdditions, totalDeletions int, err error) { // Now if we call: // $ git diff --shortstat 1ebb35b98889ff77299f24d82da426b434b0cca0...788b8b1440462d477f45b0088875 // we get: // " 9902 files changed, 2034198 insertions(+), 298800 deletions(-)\n" - cmd := NewCommand("diff", "--shortstat").AddArguments(trustedArgs...).AddDynamicArguments(dynamicArgs...) - stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath}) + cmd := gitcmd.NewCommand("diff", "--shortstat").AddArguments(trustedArgs...).AddDynamicArguments(dynamicArgs...) + stdout, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if err != nil { return 0, 0, 0, err } @@ -139,8 +141,8 @@ func parseDiffStat(stdout string) (numFiles, totalAdditions, totalDeletions int, // GetDiff generates and returns patch data between given revisions, optimized for human readability func (repo *Repository) GetDiff(compareArg string, w io.Writer) error { stderr := new(bytes.Buffer) - return NewCommand("diff", "-p").AddDynamicArguments(compareArg). - Run(repo.Ctx, &RunOpts{ + return gitcmd.NewCommand("diff", "-p").AddDynamicArguments(compareArg). + Run(repo.Ctx, &gitcmd.RunOpts{ Dir: repo.Path, Stdout: w, Stderr: stderr, @@ -149,7 +151,7 @@ func (repo *Repository) GetDiff(compareArg string, w io.Writer) error { // GetDiffBinary generates and returns patch data between given revisions, including binary diffs. func (repo *Repository) GetDiffBinary(compareArg string, w io.Writer) error { - return NewCommand("diff", "-p", "--binary", "--histogram").AddDynamicArguments(compareArg).Run(repo.Ctx, &RunOpts{ + return gitcmd.NewCommand("diff", "-p", "--binary", "--histogram").AddDynamicArguments(compareArg).Run(repo.Ctx, &gitcmd.RunOpts{ Dir: repo.Path, Stdout: w, }) @@ -158,8 +160,8 @@ func (repo *Repository) GetDiffBinary(compareArg string, w io.Writer) error { // GetPatch generates and returns format-patch data between given revisions, able to be used with `git apply` func (repo *Repository) GetPatch(compareArg string, w io.Writer) error { stderr := new(bytes.Buffer) - return NewCommand("format-patch", "--binary", "--stdout").AddDynamicArguments(compareArg). - Run(repo.Ctx, &RunOpts{ + return gitcmd.NewCommand("format-patch", "--binary", "--stdout").AddDynamicArguments(compareArg). + Run(repo.Ctx, &gitcmd.RunOpts{ Dir: repo.Path, Stdout: w, Stderr: stderr, @@ -174,13 +176,13 @@ func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, err if err != nil { return nil, err } - cmd := NewCommand("diff-tree", "--name-only", "--root", "--no-commit-id", "-r", "-z") + cmd := gitcmd.NewCommand("diff-tree", "--name-only", "--root", "--no-commit-id", "-r", "-z") if base == objectFormat.EmptyObjectID().String() { cmd.AddDynamicArguments(head) } else { cmd.AddDynamicArguments(base, head) } - stdout, _, err := cmd.RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err := cmd.RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil { return nil, err } diff --git a/modules/git/repo_compare_test.go b/modules/git/repo_compare_test.go index 35636fae1a7..47fd2ca102f 100644 --- a/modules/git/repo_compare_test.go +++ b/modules/git/repo_compare_test.go @@ -9,6 +9,8 @@ import ( "path/filepath" "testing" + "code.gitea.io/gitea/modules/git/gitcmd" + "github.com/stretchr/testify/assert" ) @@ -99,7 +101,9 @@ func TestReadWritePullHead(t *testing.T) { // Write a fake sha1 with only 40 zeros newCommit := "feaf4ba6bc635fec442f46ddd4512416ec43c2c2" - err = repo.SetReference(PullPrefix+"1/head", newCommit) + _, _, err = gitcmd.NewCommand("update-ref"). + AddDynamicArguments(PullPrefix+"1/head", newCommit). + RunStdString(t.Context(), &gitcmd.RunOpts{Dir: repo.Path}) if err != nil { assert.NoError(t, err) return @@ -116,7 +120,9 @@ func TestReadWritePullHead(t *testing.T) { assert.Equal(t, headContents, newCommit) // Remove file after the test - err = repo.RemoveReference(PullPrefix + "1/head") + _, _, err = gitcmd.NewCommand("update-ref", "--no-deref", "-d"). + AddDynamicArguments(PullPrefix+"1/head"). + RunStdString(t.Context(), &gitcmd.RunOpts{Dir: repo.Path}) assert.NoError(t, err) } diff --git a/modules/git/repo_gpg.go b/modules/git/repo_gpg.go index 0021a7bda7b..a999d2dbc60 100644 --- a/modules/git/repo_gpg.go +++ b/modules/git/repo_gpg.go @@ -9,6 +9,7 @@ import ( "os" "strings" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/process" ) @@ -42,7 +43,7 @@ func (repo *Repository) GetDefaultPublicGPGKey(forceUpdate bool) (*GPGSettings, Sign: true, } - value, _, _ := NewCommand("config", "--get", "commit.gpgsign").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + value, _, _ := gitcmd.NewCommand("config", "--get", "commit.gpgsign").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) sign, valid := ParseBool(strings.TrimSpace(value)) if !sign || !valid { gpgSettings.Sign = false @@ -50,16 +51,16 @@ func (repo *Repository) GetDefaultPublicGPGKey(forceUpdate bool) (*GPGSettings, return gpgSettings, nil } - signingKey, _, _ := NewCommand("config", "--get", "user.signingkey").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + signingKey, _, _ := gitcmd.NewCommand("config", "--get", "user.signingkey").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) gpgSettings.KeyID = strings.TrimSpace(signingKey) - format, _, _ := NewCommand("config", "--default", SigningKeyFormatOpenPGP, "--get", "gpg.format").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + format, _, _ := gitcmd.NewCommand("config", "--default", SigningKeyFormatOpenPGP, "--get", "gpg.format").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) gpgSettings.Format = strings.TrimSpace(format) - defaultEmail, _, _ := NewCommand("config", "--get", "user.email").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + defaultEmail, _, _ := gitcmd.NewCommand("config", "--get", "user.email").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) gpgSettings.Email = strings.TrimSpace(defaultEmail) - defaultName, _, _ := NewCommand("config", "--get", "user.name").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + defaultName, _, _ := gitcmd.NewCommand("config", "--get", "user.name").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) gpgSettings.Name = strings.TrimSpace(defaultName) if err := gpgSettings.LoadPublicKeyContent(); err != nil { diff --git a/modules/git/repo_index.go b/modules/git/repo_index.go index 4879121a418..e7b3792d95a 100644 --- a/modules/git/repo_index.go +++ b/modules/git/repo_index.go @@ -10,6 +10,7 @@ import ( "path/filepath" "strings" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/setting" ) @@ -21,7 +22,7 @@ func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string) } if len(treeish) != objectFormat.FullLength() { - res, _, err := NewCommand("rev-parse", "--verify").AddDynamicArguments(treeish).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + res, _, err := gitcmd.NewCommand("rev-parse", "--verify").AddDynamicArguments(treeish).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil { return err } @@ -41,7 +42,7 @@ func (repo *Repository) readTreeToIndex(id ObjectID, indexFilename ...string) er if len(indexFilename) > 0 { env = append(os.Environ(), "GIT_INDEX_FILE="+indexFilename[0]) } - _, _, err := NewCommand("read-tree").AddDynamicArguments(id.String()).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path, Env: env}) + _, _, err := gitcmd.NewCommand("read-tree").AddDynamicArguments(id.String()).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path, Env: env}) if err != nil { return err } @@ -74,14 +75,14 @@ func (repo *Repository) ReadTreeToTemporaryIndex(treeish string) (tmpIndexFilena // EmptyIndex empties the index func (repo *Repository) EmptyIndex() error { - _, _, err := NewCommand("read-tree", "--empty").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + _, _, err := gitcmd.NewCommand("read-tree", "--empty").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) return err } // LsFiles checks if the given filenames are in the index func (repo *Repository) LsFiles(filenames ...string) ([]string, error) { - cmd := NewCommand("ls-files", "-z").AddDashesAndList(filenames...) - res, _, err := cmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + cmd := gitcmd.NewCommand("ls-files", "-z").AddDashesAndList(filenames...) + res, _, err := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil { return nil, err } @@ -99,7 +100,7 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error { if err != nil { return err } - cmd := NewCommand("update-index", "--remove", "-z", "--index-info") + cmd := gitcmd.NewCommand("update-index", "--remove", "-z", "--index-info") stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) buffer := new(bytes.Buffer) @@ -109,7 +110,7 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error { buffer.WriteString("0 blob " + objectFormat.EmptyObjectID().String() + "\t" + file + "\000") } } - return cmd.Run(repo.Ctx, &RunOpts{ + return cmd.Run(repo.Ctx, &gitcmd.RunOpts{ Dir: repo.Path, Stdin: bytes.NewReader(buffer.Bytes()), Stdout: stdout, @@ -125,7 +126,7 @@ type IndexObjectInfo struct { // AddObjectsToIndex adds the provided object hashes to the index at the provided filenames func (repo *Repository) AddObjectsToIndex(objects ...IndexObjectInfo) error { - cmd := NewCommand("update-index", "--add", "--replace", "-z", "--index-info") + cmd := gitcmd.NewCommand("update-index", "--add", "--replace", "-z", "--index-info") stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) buffer := new(bytes.Buffer) @@ -133,7 +134,7 @@ func (repo *Repository) AddObjectsToIndex(objects ...IndexObjectInfo) error { // using format: mode SP type SP sha1 TAB path buffer.WriteString(object.Mode + " blob " + object.Object.String() + "\t" + object.Filename + "\000") } - return cmd.Run(repo.Ctx, &RunOpts{ + return cmd.Run(repo.Ctx, &gitcmd.RunOpts{ Dir: repo.Path, Stdin: bytes.NewReader(buffer.Bytes()), Stdout: stdout, @@ -148,7 +149,7 @@ func (repo *Repository) AddObjectToIndex(mode string, object ObjectID, filename // WriteTree writes the current index as a tree to the object db and returns its hash func (repo *Repository) WriteTree() (*Tree, error) { - stdout, _, runErr := NewCommand("write-tree").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, runErr := gitcmd.NewCommand("write-tree").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if runErr != nil { return nil, runErr } diff --git a/modules/git/repo_object.go b/modules/git/repo_object.go index 08e0413311e..e8f6510c237 100644 --- a/modules/git/repo_object.go +++ b/modules/git/repo_object.go @@ -8,6 +8,8 @@ import ( "bytes" "io" "strings" + + "code.gitea.io/gitea/modules/git/gitcmd" ) // ObjectType git object type @@ -66,15 +68,15 @@ func (repo *Repository) HashObject(reader io.Reader) (ObjectID, error) { } func (repo *Repository) hashObject(reader io.Reader, save bool) (string, error) { - var cmd *Command + var cmd *gitcmd.Command if save { - cmd = NewCommand("hash-object", "-w", "--stdin") + cmd = gitcmd.NewCommand("hash-object", "-w", "--stdin") } else { - cmd = NewCommand("hash-object", "--stdin") + cmd = gitcmd.NewCommand("hash-object", "--stdin") } stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) - err := cmd.Run(repo.Ctx, &RunOpts{ + err := cmd.Run(repo.Ctx, &gitcmd.RunOpts{ Dir: repo.Path, Stdin: reader, Stdout: stdout, diff --git a/modules/git/repo_ref.go b/modules/git/repo_ref.go index 554f9f73e11..577e17c45df 100644 --- a/modules/git/repo_ref.go +++ b/modules/git/repo_ref.go @@ -7,6 +7,7 @@ import ( "context" "strings" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/util" ) @@ -18,7 +19,7 @@ func (repo *Repository) GetRefs() ([]*Reference, error) { // ListOccurrences lists all refs of the given refType the given commit appears in sorted by creation date DESC // refType should only be a literal "branch" or "tag" and nothing else func (repo *Repository) ListOccurrences(ctx context.Context, refType, commitSHA string) ([]string, error) { - cmd := NewCommand() + cmd := gitcmd.NewCommand() switch refType { case "branch": cmd.AddArguments("branch") @@ -27,7 +28,7 @@ func (repo *Repository) ListOccurrences(ctx context.Context, refType, commitSHA default: return nil, util.NewInvalidArgumentErrorf(`can only use "branch" or "tag" for refType, but got %q`, refType) } - stdout, _, err := cmd.AddArguments("--no-color", "--sort=-creatordate", "--contains").AddDynamicArguments(commitSHA).RunStdString(ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err := cmd.AddArguments("--no-color", "--sort=-creatordate", "--contains").AddDynamicArguments(commitSHA).RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil { return nil, err } diff --git a/modules/git/repo_ref_nogogit.go b/modules/git/repo_ref_nogogit.go index 8d34713eaf3..784efecc654 100644 --- a/modules/git/repo_ref_nogogit.go +++ b/modules/git/repo_ref_nogogit.go @@ -9,6 +9,8 @@ import ( "bufio" "io" "strings" + + "code.gitea.io/gitea/modules/git/gitcmd" ) // GetRefsFiltered returns all references of the repository that matches patterm exactly or starting with. @@ -21,13 +23,13 @@ func (repo *Repository) GetRefsFiltered(pattern string) ([]*Reference, error) { go func() { stderrBuilder := &strings.Builder{} - err := NewCommand("for-each-ref").Run(repo.Ctx, &RunOpts{ + err := gitcmd.NewCommand("for-each-ref").Run(repo.Ctx, &gitcmd.RunOpts{ Dir: repo.Path, Stdout: stdoutWriter, Stderr: stderrBuilder, }) if err != nil { - _ = stdoutWriter.CloseWithError(ConcatenateError(err, stderrBuilder.String())) + _ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, stderrBuilder.String())) } else { _ = stdoutWriter.Close() } diff --git a/modules/git/repo_stats.go b/modules/git/repo_stats.go index 8c6f31c38c8..22082325efb 100644 --- a/modules/git/repo_stats.go +++ b/modules/git/repo_stats.go @@ -14,6 +14,7 @@ import ( "time" "code.gitea.io/gitea/modules/container" + "code.gitea.io/gitea/modules/git/gitcmd" ) // CodeActivityStats represents git statistics data @@ -40,9 +41,9 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string) since := fromTime.Format(time.RFC3339) - stdout, _, runErr := NewCommand("rev-list", "--count", "--no-merges", "--branches=*", "--date=iso"). + stdout, _, runErr := gitcmd.NewCommand("rev-list", "--count", "--no-merges", "--branches=*", "--date=iso"). AddOptionFormat("--since=%s", since). - RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if runErr != nil { return nil, runErr } @@ -62,7 +63,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string) _ = stdoutWriter.Close() }() - gitCmd := NewCommand("log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso"). + gitCmd := gitcmd.NewCommand("log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso"). AddOptionFormat("--since=%s", since) if len(branch) == 0 { gitCmd.AddArguments("--branches=*") @@ -71,7 +72,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string) } stderr := new(strings.Builder) - err = gitCmd.Run(repo.Ctx, &RunOpts{ + err = gitCmd.Run(repo.Ctx, &gitcmd.RunOpts{ Env: []string{}, Dir: repo.Path, Stdout: stdoutWriter, diff --git a/modules/git/repo_tag.go b/modules/git/repo_tag.go index c8d72eee024..0cb09324590 100644 --- a/modules/git/repo_tag.go +++ b/modules/git/repo_tag.go @@ -10,6 +10,7 @@ import ( "strings" "code.gitea.io/gitea/modules/git/foreachref" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/util" ) @@ -18,13 +19,13 @@ const TagPrefix = "refs/tags/" // CreateTag create one tag in the repository func (repo *Repository) CreateTag(name, revision string) error { - _, _, err := NewCommand("tag").AddDashesAndList(name, revision).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + _, _, err := gitcmd.NewCommand("tag").AddDashesAndList(name, revision).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) return err } // CreateAnnotatedTag create one annotated tag in the repository func (repo *Repository) CreateAnnotatedTag(name, message, revision string) error { - _, _, err := NewCommand("tag", "-a", "-m").AddDynamicArguments(message).AddDashesAndList(name, revision).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + _, _, err := gitcmd.NewCommand("tag", "-a", "-m").AddDynamicArguments(message).AddDashesAndList(name, revision).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) return err } @@ -34,7 +35,7 @@ func (repo *Repository) GetTagNameBySHA(sha string) (string, error) { return "", fmt.Errorf("SHA is too short: %s", sha) } - stdout, _, err := NewCommand("show-ref", "--tags", "-d").RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err := gitcmd.NewCommand("show-ref", "--tags", "-d").RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil { return "", err } @@ -57,7 +58,7 @@ func (repo *Repository) GetTagNameBySHA(sha string) (string, error) { // GetTagID returns the object ID for a tag (annotated tags have both an object SHA AND a commit SHA) func (repo *Repository) GetTagID(name string) (string, error) { - stdout, _, err := NewCommand("show-ref", "--tags").AddDashesAndList(name).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + stdout, _, err := gitcmd.NewCommand("show-ref", "--tags").AddDashesAndList(name).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil { return "", err } @@ -114,14 +115,14 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) { defer stdoutReader.Close() defer stdoutWriter.Close() stderr := strings.Builder{} - rc := &RunOpts{Dir: repo.Path, Stdout: stdoutWriter, Stderr: &stderr} + rc := &gitcmd.RunOpts{Dir: repo.Path, Stdout: stdoutWriter, Stderr: &stderr} go func() { - err := NewCommand("for-each-ref"). + err := gitcmd.NewCommand("for-each-ref"). AddOptionFormat("--format=%s", forEachRefFmt.Flag()). AddArguments("--sort", "-*creatordate", "refs/tags").Run(repo.Ctx, rc) if err != nil { - _ = stdoutWriter.CloseWithError(ConcatenateError(err, stderr.String())) + _ = stdoutWriter.CloseWithError(gitcmd.ConcatenateError(err, stderr.String())) } else { _ = stdoutWriter.Close() } diff --git a/modules/git/repo_tree.go b/modules/git/repo_tree.go index 309a73d7595..1d8c9409518 100644 --- a/modules/git/repo_tree.go +++ b/modules/git/repo_tree.go @@ -9,6 +9,8 @@ import ( "os" "strings" "time" + + "code.gitea.io/gitea/modules/git/gitcmd" ) // CommitTreeOpts represents the possible options to CommitTree @@ -33,7 +35,7 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt "GIT_COMMITTER_EMAIL="+committer.Email, "GIT_COMMITTER_DATE="+commitTimeStr, ) - cmd := NewCommand("commit-tree").AddDynamicArguments(tree.ID.String()) + cmd := gitcmd.NewCommand("commit-tree").AddDynamicArguments(tree.ID.String()) for _, parent := range opts.Parents { cmd.AddArguments("-p").AddDynamicArguments(parent) @@ -58,7 +60,7 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) - err := cmd.Run(repo.Ctx, &RunOpts{ + err := cmd.Run(repo.Ctx, &gitcmd.RunOpts{ Env: env, Dir: repo.Path, Stdin: messageBytes, @@ -66,7 +68,7 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt Stderr: stderr, }) if err != nil { - return nil, ConcatenateError(err, stderr.String()) + return nil, gitcmd.ConcatenateError(err, stderr.String()) } return NewIDFromString(strings.TrimSpace(stdout.String())) } diff --git a/modules/git/repo_tree_gogit.go b/modules/git/repo_tree_gogit.go index f77cd836124..40524d0c344 100644 --- a/modules/git/repo_tree_gogit.go +++ b/modules/git/repo_tree_gogit.go @@ -9,6 +9,8 @@ package git import ( "errors" + "code.gitea.io/gitea/modules/git/gitcmd" + "github.com/go-git/go-git/v5/plumbing" ) @@ -36,7 +38,7 @@ func (repo *Repository) GetTree(idStr string) (*Tree, error) { } if len(idStr) != objectFormat.FullLength() { - res, _, err := NewCommand("rev-parse", "--verify").AddDynamicArguments(idStr).RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + res, _, err := gitcmd.NewCommand("rev-parse", "--verify").AddDynamicArguments(idStr).RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil { return nil, err } diff --git a/modules/git/submodule.go b/modules/git/submodule.go index 31a32f1a9e2..58824adc825 100644 --- a/modules/git/submodule.go +++ b/modules/git/submodule.go @@ -9,6 +9,7 @@ import ( "fmt" "os" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" ) @@ -24,7 +25,7 @@ func GetTemplateSubmoduleCommits(ctx context.Context, repoPath string) (submodul if err != nil { return nil, err } - opts := &RunOpts{ + opts := &gitcmd.RunOpts{ Dir: repoPath, Stdout: stdoutWriter, PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { @@ -45,7 +46,7 @@ func GetTemplateSubmoduleCommits(ctx context.Context, repoPath string) (submodul return scanner.Err() }, } - err = NewCommand("ls-tree", "-r", "--", "HEAD").Run(ctx, opts) + err = gitcmd.NewCommand("ls-tree", "-r", "--", "HEAD").Run(ctx, opts) if err != nil { return nil, fmt.Errorf("GetTemplateSubmoduleCommits: error running git ls-tree: %v", err) } @@ -56,8 +57,8 @@ func GetTemplateSubmoduleCommits(ctx context.Context, repoPath string) (submodul // It is only for generating new repos based on existing template, requires the .gitmodules file to be already present in the work dir. func AddTemplateSubmoduleIndexes(ctx context.Context, repoPath string, submodules []TemplateSubmoduleCommit) error { for _, submodule := range submodules { - cmd := NewCommand("update-index", "--add", "--cacheinfo", "160000").AddDynamicArguments(submodule.Commit, submodule.Path) - if stdout, _, err := cmd.RunStdString(ctx, &RunOpts{Dir: repoPath}); err != nil { + cmd := gitcmd.NewCommand("update-index", "--add", "--cacheinfo", "160000").AddDynamicArguments(submodule.Commit, submodule.Path) + if stdout, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}); err != nil { log.Error("Unable to add %s as submodule to repo %s: stdout %s\nError: %v", submodule.Path, repoPath, stdout, err) return err } diff --git a/modules/git/submodule_test.go b/modules/git/submodule_test.go index 55ac0971a5c..d2df8b2a911 100644 --- a/modules/git/submodule_test.go +++ b/modules/git/submodule_test.go @@ -8,6 +8,8 @@ import ( "path/filepath" "testing" + "code.gitea.io/gitea/modules/git/gitcmd" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -30,14 +32,14 @@ func TestAddTemplateSubmoduleIndexes(t *testing.T) { ctx := t.Context() tmpDir := t.TempDir() var err error - _, _, err = NewCommand("init").RunStdString(ctx, &RunOpts{Dir: tmpDir}) + _, _, err = gitcmd.NewCommand("init").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpDir}) require.NoError(t, err) _ = os.Mkdir(filepath.Join(tmpDir, "new-dir"), 0o755) err = AddTemplateSubmoduleIndexes(ctx, tmpDir, []TemplateSubmoduleCommit{{Path: "new-dir", Commit: "1234567890123456789012345678901234567890"}}) require.NoError(t, err) - _, _, err = NewCommand("add", "--all").RunStdString(ctx, &RunOpts{Dir: tmpDir}) + _, _, err = gitcmd.NewCommand("add", "--all").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpDir}) require.NoError(t, err) - _, _, err = NewCommand("-c", "user.name=a", "-c", "user.email=b", "commit", "-m=test").RunStdString(ctx, &RunOpts{Dir: tmpDir}) + _, _, err = gitcmd.NewCommand("-c", "user.name=a", "-c", "user.email=b", "commit", "-m=test").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpDir}) require.NoError(t, err) submodules, err := GetTemplateSubmoduleCommits(t.Context(), tmpDir) require.NoError(t, err) diff --git a/modules/git/tree.go b/modules/git/tree.go index 38fb45f3b11..a8c4929c7c9 100644 --- a/modules/git/tree.go +++ b/modules/git/tree.go @@ -7,6 +7,8 @@ package git import ( "bytes" "strings" + + "code.gitea.io/gitea/modules/git/gitcmd" ) // NewTree create a new tree according the repository and tree id @@ -48,10 +50,10 @@ func (t *Tree) SubTree(rpath string) (*Tree, error) { // LsTree checks if the given filenames are in the tree func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error) { - cmd := NewCommand("ls-tree", "-z", "--name-only"). + cmd := gitcmd.NewCommand("ls-tree", "-z", "--name-only"). AddDashesAndList(append([]string{ref}, filenames...)...) - res, _, err := cmd.RunStdBytes(repo.Ctx, &RunOpts{Dir: repo.Path}) + res, _, err := cmd.RunStdBytes(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil { return nil, err } @@ -65,9 +67,9 @@ func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error // GetTreePathLatestCommit returns the latest commit of a tree path func (repo *Repository) GetTreePathLatestCommit(refName, treePath string) (*Commit, error) { - stdout, _, err := NewCommand("rev-list", "-1"). + stdout, _, err := gitcmd.NewCommand("rev-list", "-1"). AddDynamicArguments(refName).AddDashesAndList(treePath). - RunStdString(repo.Ctx, &RunOpts{Dir: repo.Path}) + RunStdString(repo.Ctx, &gitcmd.RunOpts{Dir: repo.Path}) if err != nil { return nil, err } diff --git a/modules/git/tree_nogogit.go b/modules/git/tree_nogogit.go index f88788418e2..045d78c42c6 100644 --- a/modules/git/tree_nogogit.go +++ b/modules/git/tree_nogogit.go @@ -8,6 +8,8 @@ package git import ( "io" "strings" + + "code.gitea.io/gitea/modules/git/gitcmd" ) // Tree represents a flat directory listing. @@ -70,7 +72,7 @@ func (t *Tree) ListEntries() (Entries, error) { } } - stdout, _, runErr := NewCommand("ls-tree", "-l").AddDynamicArguments(t.ID.String()).RunStdBytes(t.repo.Ctx, &RunOpts{Dir: t.repo.Path}) + stdout, _, runErr := gitcmd.NewCommand("ls-tree", "-l").AddDynamicArguments(t.ID.String()).RunStdBytes(t.repo.Ctx, &gitcmd.RunOpts{Dir: t.repo.Path}) if runErr != nil { if strings.Contains(runErr.Error(), "fatal: Not a valid object name") || strings.Contains(runErr.Error(), "fatal: not a tree object") { return nil, ErrNotExist{ @@ -91,15 +93,15 @@ func (t *Tree) ListEntries() (Entries, error) { // listEntriesRecursive returns all entries of current tree recursively including all subtrees // extraArgs could be "-l" to get the size, which is slower -func (t *Tree) listEntriesRecursive(extraArgs TrustedCmdArgs) (Entries, error) { +func (t *Tree) listEntriesRecursive(extraArgs gitcmd.TrustedCmdArgs) (Entries, error) { if t.entriesRecursiveParsed { return t.entriesRecursive, nil } - stdout, _, runErr := NewCommand("ls-tree", "-t", "-r"). + stdout, _, runErr := gitcmd.NewCommand("ls-tree", "-t", "-r"). AddArguments(extraArgs...). AddDynamicArguments(t.ID.String()). - RunStdBytes(t.repo.Ctx, &RunOpts{Dir: t.repo.Path}) + RunStdBytes(t.repo.Ctx, &gitcmd.RunOpts{Dir: t.repo.Path}) if runErr != nil { return nil, runErr } @@ -120,5 +122,5 @@ func (t *Tree) ListEntriesRecursiveFast() (Entries, error) { // ListEntriesRecursiveWithSize returns all entries of current tree recursively including all subtrees, with size func (t *Tree) ListEntriesRecursiveWithSize() (Entries, error) { - return t.listEntriesRecursive(TrustedCmdArgs{"--long"}) + return t.listEntriesRecursive(gitcmd.TrustedCmdArgs{"--long"}) } diff --git a/modules/git/utils.go b/modules/git/utils.go index e2bdf7866ba..b5f188904a7 100644 --- a/modules/git/utils.go +++ b/modules/git/utils.go @@ -6,7 +6,6 @@ package git import ( "crypto/sha1" "encoding/hex" - "fmt" "io" "strconv" "strings" @@ -42,14 +41,6 @@ func (oc *ObjectCache[T]) Get(id string) (T, bool) { return obj, has } -// ConcatenateError concatenats an error with stderr string -func ConcatenateError(err error, stderr string) error { - if len(stderr) == 0 { - return err - } - return fmt.Errorf("%w - %s", err, stderr) -} - // ParseBool returns the boolean value represented by the string as per git's git_config_bool // true will be returned for the result if the string is empty, but valid will be false. // "true", "yes", "on" are all true, true diff --git a/modules/gitrepo/branch.go b/modules/gitrepo/branch.go index d7857819e49..4d571f583d0 100644 --- a/modules/gitrepo/branch.go +++ b/modules/gitrepo/branch.go @@ -7,6 +7,7 @@ import ( "context" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" ) // GetBranchesByPath returns a branch by its path @@ -33,9 +34,9 @@ func GetBranchCommitID(ctx context.Context, repo Repository, branch string) (str // SetDefaultBranch sets default branch of repository. func SetDefaultBranch(ctx context.Context, repo Repository, name string) error { - _, _, err := git.NewCommand("symbolic-ref", "HEAD"). + _, _, err := gitcmd.NewCommand("symbolic-ref", "HEAD"). AddDynamicArguments(git.BranchPrefix+name). - RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)}) + RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath(repo)}) return err } diff --git a/modules/gitrepo/config.go b/modules/gitrepo/config.go index 84ef1eadf67..c028c6afe8f 100644 --- a/modules/gitrepo/config.go +++ b/modules/gitrepo/config.go @@ -7,14 +7,14 @@ import ( "context" "strings" - "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/globallock" ) func GitConfigGet(ctx context.Context, repo Repository, key string) (string, error) { - result, _, err := git.NewCommand("config", "--get"). + result, _, err := gitcmd.NewCommand("config", "--get"). AddDynamicArguments(key). - RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)}) + RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath(repo)}) if err != nil { return "", err } @@ -28,9 +28,9 @@ func getRepoConfigLockKey(repoStoragePath string) string { // GitConfigAdd add a git configuration key to a specific value for the given repository. func GitConfigAdd(ctx context.Context, repo Repository, key, value string) error { return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error { - _, _, err := git.NewCommand("config", "--add"). + _, _, err := gitcmd.NewCommand("config", "--add"). AddDynamicArguments(key, value). - RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)}) + RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath(repo)}) return err }) } @@ -40,9 +40,9 @@ func GitConfigAdd(ctx context.Context, repo Repository, key, value string) error // If the key exists, it will be updated to the new value. func GitConfigSet(ctx context.Context, repo Repository, key, value string) error { return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error { - _, _, err := git.NewCommand("config"). + _, _, err := gitcmd.NewCommand("config"). AddDynamicArguments(key, value). - RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)}) + RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath(repo)}) return err }) } diff --git a/modules/gitrepo/fsck.go b/modules/gitrepo/fsck.go index bcec6aa03f1..ffccff28a9d 100644 --- a/modules/gitrepo/fsck.go +++ b/modules/gitrepo/fsck.go @@ -7,10 +7,10 @@ import ( "context" "time" - "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" ) // Fsck verifies the connectivity and validity of the objects in the database -func Fsck(ctx context.Context, repo Repository, timeout time.Duration, args git.TrustedCmdArgs) error { - return git.NewCommand("fsck").AddArguments(args...).Run(ctx, &git.RunOpts{Timeout: timeout, Dir: repoPath(repo)}) +func Fsck(ctx context.Context, repo Repository, timeout time.Duration, args gitcmd.TrustedCmdArgs) error { + return gitcmd.NewCommand("fsck").AddArguments(args...).Run(ctx, &gitcmd.RunOpts{Timeout: timeout, Dir: repoPath(repo)}) } diff --git a/modules/gitrepo/ref.go b/modules/gitrepo/ref.go new file mode 100644 index 00000000000..babef8b65f0 --- /dev/null +++ b/modules/gitrepo/ref.go @@ -0,0 +1,21 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitrepo + +import ( + "context" + + "code.gitea.io/gitea/modules/git/gitcmd" +) + +func UpdateRef(ctx context.Context, repo Repository, refName, newCommitID string) error { + _, _, err := gitcmd.NewCommand("update-ref").AddDynamicArguments(refName, newCommitID).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath(repo)}) + return err +} + +func RemoveRef(ctx context.Context, repo Repository, refName string) error { + _, _, err := gitcmd.NewCommand("update-ref", "--no-deref", "-d"). + AddDynamicArguments(refName).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath(repo)}) + return err +} diff --git a/modules/gitrepo/remote.go b/modules/gitrepo/remote.go index ba32c2a1d83..20d3cd21da3 100644 --- a/modules/gitrepo/remote.go +++ b/modules/gitrepo/remote.go @@ -10,6 +10,7 @@ import ( "time" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" giturl "code.gitea.io/gitea/modules/git/url" "code.gitea.io/gitea/modules/globallock" "code.gitea.io/gitea/modules/util" @@ -24,7 +25,7 @@ const ( func GitRemoteAdd(ctx context.Context, repo Repository, remoteName, remoteURL string, options ...RemoteOption) error { return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error { - cmd := git.NewCommand("remote", "add") + cmd := gitcmd.NewCommand("remote", "add") if len(options) > 0 { switch options[0] { case RemoteOptionMirrorPush: @@ -37,15 +38,15 @@ func GitRemoteAdd(ctx context.Context, repo Repository, remoteName, remoteURL st } _, _, err := cmd. AddDynamicArguments(remoteName, remoteURL). - RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)}) + RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath(repo)}) return err }) } func GitRemoteRemove(ctx context.Context, repo Repository, remoteName string) error { return globallock.LockAndDo(ctx, getRepoConfigLockKey(repo.RelativePath()), func(ctx context.Context) error { - cmd := git.NewCommand("remote", "rm").AddDynamicArguments(remoteName) - _, _, err := cmd.RunStdString(ctx, &git.RunOpts{Dir: repoPath(repo)}) + cmd := gitcmd.NewCommand("remote", "rm").AddDynamicArguments(remoteName) + _, _, err := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath(repo)}) return err }) } @@ -64,8 +65,8 @@ func GitRemoteGetURL(ctx context.Context, repo Repository, remoteName string) (* // GitRemotePrune prunes the remote branches that no longer exist in the remote repository. func GitRemotePrune(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error { - return git.NewCommand("remote", "prune").AddDynamicArguments(remoteName). - Run(ctx, &git.RunOpts{ + return gitcmd.NewCommand("remote", "prune").AddDynamicArguments(remoteName). + Run(ctx, &gitcmd.RunOpts{ Timeout: timeout, Dir: repoPath(repo), Stdout: stdout, @@ -75,8 +76,8 @@ func GitRemotePrune(ctx context.Context, repo Repository, remoteName string, tim // GitRemoteUpdatePrune updates the remote branches and prunes the ones that no longer exist in the remote repository. func GitRemoteUpdatePrune(ctx context.Context, repo Repository, remoteName string, timeout time.Duration, stdout, stderr io.Writer) error { - return git.NewCommand("remote", "update", "--prune").AddDynamicArguments(remoteName). - Run(ctx, &git.RunOpts{ + return gitcmd.NewCommand("remote", "update", "--prune").AddDynamicArguments(remoteName). + Run(ctx, &gitcmd.RunOpts{ Timeout: timeout, Dir: repoPath(repo), Stdout: stdout, diff --git a/modules/glob/glob.go b/modules/glob/glob.go new file mode 100644 index 00000000000..d4ca77e2ee1 --- /dev/null +++ b/modules/glob/glob.go @@ -0,0 +1,184 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package glob + +import ( + "errors" + "fmt" + "regexp" + + "code.gitea.io/gitea/modules/util" +) + +// Reference: https://github.com/gobwas/glob/blob/master/glob.go + +type Glob interface { + Match(string) bool +} + +type globCompiler struct { + nonSeparatorChars string + globPattern []rune + regexpPattern string + regexp *regexp.Regexp + pos int +} + +// compileChars compiles character class patterns like [abc] or [!abc] +func (g *globCompiler) compileChars() (string, error) { + result := "" + if g.pos < len(g.globPattern) && g.globPattern[g.pos] == '!' { + g.pos++ + result += "^" + } + + for g.pos < len(g.globPattern) { + c := g.globPattern[g.pos] + g.pos++ + + if c == ']' { + return "[" + result + "]", nil + } + + if c == '\\' { + if g.pos >= len(g.globPattern) { + return "", errors.New("unterminated character class escape") + } + result += "\\" + string(g.globPattern[g.pos]) + g.pos++ + } else { + result += string(c) + } + } + + return "", errors.New("unterminated character class") +} + +// compile compiles the glob pattern into a regular expression +func (g *globCompiler) compile(subPattern bool) (string, error) { + result := "" + + for g.pos < len(g.globPattern) { + c := g.globPattern[g.pos] + g.pos++ + + if subPattern && c == '}' { + return "(" + result + ")", nil + } + + switch c { + case '*': + if g.pos < len(g.globPattern) && g.globPattern[g.pos] == '*' { + g.pos++ + result += ".*" // match any sequence of characters + } else { + result += g.nonSeparatorChars + "*" // match any sequence of non-separator characters + } + case '?': + result += g.nonSeparatorChars // match any single non-separator character + case '[': + chars, err := g.compileChars() + if err != nil { + return "", err + } + result += chars + case '{': + subResult, err := g.compile(true) + if err != nil { + return "", err + } + result += subResult + case ',': + if subPattern { + result += "|" + } else { + result += "," + } + case '\\': + if g.pos >= len(g.globPattern) { + return "", errors.New("no character to escape") + } + result += "\\" + string(g.globPattern[g.pos]) + g.pos++ + case '.', '+', '^', '$', '(', ')', '|': + result += "\\" + string(c) // escape regexp special characters + default: + result += string(c) + } + } + + return result, nil +} + +func newGlobCompiler(pattern string, separators ...rune) (Glob, error) { + g := &globCompiler{globPattern: []rune(pattern)} + + // Escape separators for use in character class + escapedSeparators := regexp.QuoteMeta(string(separators)) + if escapedSeparators != "" { + g.nonSeparatorChars = "[^" + escapedSeparators + "]" + } else { + g.nonSeparatorChars = "." + } + + compiled, err := g.compile(false) + if err != nil { + return nil, err + } + + g.regexpPattern = "^" + compiled + "$" + + regex, err := regexp.Compile(g.regexpPattern) + if err != nil { + return nil, fmt.Errorf("failed to compile regexp: %w", err) + } + + g.regexp = regex + return g, nil +} + +func (g *globCompiler) Match(s string) bool { + return g.regexp.MatchString(s) +} + +func Compile(pattern string, separators ...rune) (Glob, error) { + return newGlobCompiler(pattern, separators...) +} + +func MustCompile(pattern string, separators ...rune) Glob { + g, err := Compile(pattern, separators...) + if err != nil { + panic(err) + } + return g +} + +func IsSpecialByte(c byte) bool { + return c == '*' || c == '?' || c == '\\' || c == '[' || c == ']' || c == '{' || c == '}' +} + +// QuoteMeta returns a string that quotes all glob pattern meta characters +// inside the argument text; For example, QuoteMeta(`{foo*}`) returns `\[foo\*\]`. +// Reference: https://github.com/gobwas/glob/blob/master/glob.go +func QuoteMeta(s string) string { + pos := 0 + for pos < len(s) && !IsSpecialByte(s[pos]) { + pos++ + } + if pos == len(s) { + return s + } + b := make([]byte, pos+2*(len(s)-pos)) + copy(b, s[0:pos]) + to := pos + for ; pos < len(s); pos++ { + if IsSpecialByte(s[pos]) { + b[to] = '\\' + to++ + } + b[to] = s[pos] + to++ + } + return util.UnsafeBytesToString(b[0:to]) +} diff --git a/modules/glob/glob_test.go b/modules/glob/glob_test.go new file mode 100644 index 00000000000..84678952524 --- /dev/null +++ b/modules/glob/glob_test.go @@ -0,0 +1,208 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// Copyright (c) 2016 Sergey Kamardin +// SPDX-License-Identifier: MIT +// +//nolint:revive // the code is from gobwas/glob +package glob + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Reference: https://github.com/gobwas/glob/blob/master/glob_test.go + +const ( + pattern_all = "[a-z][!a-x]*cat*[h][!b]*eyes*" + regexp_all = `^[a-z][^a-x].*cat.*[h][^b].*eyes.*$` + fixture_all_match = "my cat has very bright eyes" + fixture_all_mismatch = "my dog has very bright eyes" + + pattern_plain = "google.com" + regexp_plain = `^google\.com$` + fixture_plain_match = "google.com" + fixture_plain_mismatch = "gobwas.com" + + pattern_multiple = "https://*.google.*" + regexp_multiple = `^https:\/\/.*\.google\..*$` + fixture_multiple_match = "https://account.google.com" + fixture_multiple_mismatch = "https://google.com" + + pattern_alternatives = "{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}" + regexp_alternatives = `^(https:\/\/.*\.google\..*|.*yandex\..*|.*yahoo\..*|.*mail\.ru)$` + fixture_alternatives_match = "http://yahoo.com" + fixture_alternatives_mismatch = "http://google.com" + + pattern_alternatives_suffix = "{https://*gobwas.com,http://exclude.gobwas.com}" + regexp_alternatives_suffix = `^(https:\/\/.*gobwas\.com|http://exclude.gobwas.com)$` + fixture_alternatives_suffix_first_match = "https://safe.gobwas.com" + fixture_alternatives_suffix_first_mismatch = "http://safe.gobwas.com" + fixture_alternatives_suffix_second = "http://exclude.gobwas.com" + + pattern_prefix = "abc*" + regexp_prefix = `^abc.*$` + pattern_suffix = "*def" + regexp_suffix = `^.*def$` + pattern_prefix_suffix = "ab*ef" + regexp_prefix_suffix = `^ab.*ef$` + fixture_prefix_suffix_match = "abcdef" + fixture_prefix_suffix_mismatch = "af" + + pattern_alternatives_combine_lite = "{abc*def,abc?def,abc[zte]def}" + regexp_alternatives_combine_lite = `^(abc.*def|abc.def|abc[zte]def)$` + fixture_alternatives_combine_lite = "abczdef" + + pattern_alternatives_combine_hard = "{abc*[a-c]def,abc?[d-g]def,abc[zte]?def}" + regexp_alternatives_combine_hard = `^(abc.*[a-c]def|abc.[d-g]def|abc[zte].def)$` + fixture_alternatives_combine_hard = "abczqdef" +) + +type test struct { + pattern, match string + should bool + delimiters []rune +} + +func glob(s bool, p, m string, d ...rune) test { + return test{p, m, s, d} +} + +func TestGlob(t *testing.T) { + for _, test := range []test{ + glob(true, "* ?at * eyes", "my cat has very bright eyes"), + + glob(true, "", ""), + glob(false, "", "b"), + + glob(true, "*ä", "åä"), + glob(true, "abc", "abc"), + glob(true, "a*c", "abc"), + glob(true, "a*c", "a12345c"), + glob(true, "a?c", "a1c"), + glob(true, "a.b", "a.b", '.'), + glob(true, "a.*", "a.b", '.'), + glob(true, "a.**", "a.b.c", '.'), + glob(true, "a.?.c", "a.b.c", '.'), + glob(true, "a.?.?", "a.b.c", '.'), + glob(true, "?at", "cat"), + glob(true, "?at", "fat"), + glob(true, "*", "abc"), + glob(true, `\*`, "*"), + glob(true, "**", "a.b.c", '.'), + + glob(false, "?at", "at"), + glob(false, "?at", "fat", 'f'), + glob(false, "a.*", "a.b.c", '.'), + glob(false, "a.?.c", "a.bb.c", '.'), + glob(false, "*", "a.b.c", '.'), + + glob(true, "*test", "this is a test"), + glob(true, "this*", "this is a test"), + glob(true, "*is *", "this is a test"), + glob(true, "*is*a*", "this is a test"), + glob(true, "**test**", "this is a test"), + glob(true, "**is**a***test*", "this is a test"), + + glob(false, "*is", "this is a test"), + glob(false, "*no*", "this is a test"), + glob(true, "[!a]*", "this is a test3"), + + glob(true, "*abc", "abcabc"), + glob(true, "**abc", "abcabc"), + glob(true, "???", "abc"), + glob(true, "?*?", "abc"), + glob(true, "?*?", "ac"), + glob(false, "sta", "stagnation"), + glob(true, "sta*", "stagnation"), + glob(false, "sta?", "stagnation"), + glob(false, "sta?n", "stagnation"), + + glob(true, "{abc,def}ghi", "defghi"), + glob(true, "{abc,abcd}a", "abcda"), + glob(true, "{a,ab}{bc,f}", "abc"), + glob(true, "{*,**}{a,b}", "ab"), + glob(false, "{*,**}{a,b}", "ac"), + + glob(true, "/{rate,[a-z][a-z][a-z]}*", "/rate"), + glob(true, "/{rate,[0-9][0-9][0-9]}*", "/rate"), + glob(true, "/{rate,[a-z][a-z][a-z]}*", "/usd"), + + glob(true, "{*.google.*,*.yandex.*}", "www.google.com", '.'), + glob(true, "{*.google.*,*.yandex.*}", "www.yandex.com", '.'), + glob(false, "{*.google.*,*.yandex.*}", "yandex.com", '.'), + glob(false, "{*.google.*,*.yandex.*}", "google.com", '.'), + + glob(true, "{*.google.*,yandex.*}", "www.google.com", '.'), + glob(true, "{*.google.*,yandex.*}", "yandex.com", '.'), + glob(false, "{*.google.*,yandex.*}", "www.yandex.com", '.'), + glob(false, "{*.google.*,yandex.*}", "google.com", '.'), + + glob(true, "*//{,*.}example.com", "https://www.example.com"), + glob(true, "*//{,*.}example.com", "http://example.com"), + glob(false, "*//{,*.}example.com", "http://example.com.net"), + + glob(true, pattern_all, fixture_all_match), + glob(false, pattern_all, fixture_all_mismatch), + + glob(true, pattern_plain, fixture_plain_match), + glob(false, pattern_plain, fixture_plain_mismatch), + + glob(true, pattern_multiple, fixture_multiple_match), + glob(false, pattern_multiple, fixture_multiple_mismatch), + + glob(true, pattern_alternatives, fixture_alternatives_match), + glob(false, pattern_alternatives, fixture_alternatives_mismatch), + + glob(true, pattern_alternatives_suffix, fixture_alternatives_suffix_first_match), + glob(false, pattern_alternatives_suffix, fixture_alternatives_suffix_first_mismatch), + glob(true, pattern_alternatives_suffix, fixture_alternatives_suffix_second), + + glob(true, pattern_alternatives_combine_hard, fixture_alternatives_combine_hard), + + glob(true, pattern_alternatives_combine_lite, fixture_alternatives_combine_lite), + + glob(true, pattern_prefix, fixture_prefix_suffix_match), + glob(false, pattern_prefix, fixture_prefix_suffix_mismatch), + + glob(true, pattern_suffix, fixture_prefix_suffix_match), + glob(false, pattern_suffix, fixture_prefix_suffix_mismatch), + + glob(true, pattern_prefix_suffix, fixture_prefix_suffix_match), + glob(false, pattern_prefix_suffix, fixture_prefix_suffix_mismatch), + } { + g, err := Compile(test.pattern, test.delimiters...) + require.NoError(t, err) + result := g.Match(test.match) + assert.Equal(t, test.should, result, "pattern %q matching %q should be %v but got %v, compiled=%s", test.pattern, test.match, test.should, result, g.(*globCompiler).regexpPattern) + } +} + +func TestQuoteMeta(t *testing.T) { + for id, test := range []struct { + in, out string + }{ + { + in: `[foo*]`, + out: `\[foo\*\]`, + }, + { + in: `{foo*}`, + out: `\{foo\*\}`, + }, + { + in: `*?\[]{}`, + out: `\*\?\\\[\]\{\}`, + }, + { + in: `some text and *?\[]{}`, + out: `some text and \*\?\\\[\]\{\}`, + }, + } { + act := QuoteMeta(test.in) + assert.Equal(t, test.out, act, "QuoteMeta(%q)", test.in) + _, err := Compile(act) + assert.NoError(t, err, "#%d _, err := Compile(QuoteMeta(%q) = %q); err = %q", id, test.in, act, err) + } +} diff --git a/modules/indexer/code/bleve/bleve.go b/modules/indexer/code/bleve/bleve.go index 70f0995a012..c233f491e3e 100644 --- a/modules/indexer/code/bleve/bleve.go +++ b/modules/indexer/code/bleve/bleve.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/analyze" "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/indexer" path_filter "code.gitea.io/gitea/modules/indexer/code/bleve/token/path" "code.gitea.io/gitea/modules/indexer/code/internal" @@ -162,7 +163,7 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro var err error if !update.Sized { var stdout string - stdout, _, err = git.NewCommand("cat-file", "-s").AddDynamicArguments(update.BlobSha).RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}) + stdout, _, err = gitcmd.NewCommand("cat-file", "-s").AddDynamicArguments(update.BlobSha).RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) if err != nil { return err } diff --git a/modules/indexer/code/elasticsearch/elasticsearch.go b/modules/indexer/code/elasticsearch/elasticsearch.go index f925ce396a3..b08d837a2a9 100644 --- a/modules/indexer/code/elasticsearch/elasticsearch.go +++ b/modules/indexer/code/elasticsearch/elasticsearch.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/analyze" "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/indexer" "code.gitea.io/gitea/modules/indexer/code/internal" indexer_internal "code.gitea.io/gitea/modules/indexer/internal" @@ -147,7 +148,7 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro var err error if !update.Sized { var stdout string - stdout, _, err = git.NewCommand("cat-file", "-s").AddDynamicArguments(update.BlobSha).RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}) + stdout, _, err = gitcmd.NewCommand("cat-file", "-s").AddDynamicArguments(update.BlobSha).RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) if err != nil { return nil, err } @@ -250,7 +251,7 @@ func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha st func (b *Indexer) Delete(ctx context.Context, repoID int64) error { if err := b.doDelete(ctx, repoID); err != nil { // Maybe there is a conflict during the delete operation, so we should retry after a refresh - log.Warn("Deletion of entries of repo %v within index %v was erroneus. Trying to refresh index before trying again", repoID, b.inner.VersionedIndexName(), err) + log.Warn("Deletion of entries of repo %v within index %v was erroneous. Trying to refresh index before trying again", repoID, b.inner.VersionedIndexName(), err) if err := b.refreshIndex(ctx); err != nil { return err } diff --git a/modules/indexer/code/git.go b/modules/indexer/code/git.go index 41bc74e6ec7..f1513d66b03 100644 --- a/modules/indexer/code/git.go +++ b/modules/indexer/code/git.go @@ -10,13 +10,14 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/indexer/code/internal" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" ) func getDefaultBranchSha(ctx context.Context, repo *repo_model.Repository) (string, error) { - stdout, _, err := git.NewCommand("show-ref", "-s").AddDynamicArguments(git.BranchPrefix+repo.DefaultBranch).RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}) + stdout, _, err := gitcmd.NewCommand("show-ref", "-s").AddDynamicArguments(git.BranchPrefix+repo.DefaultBranch).RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) if err != nil { return "", err } @@ -32,8 +33,8 @@ func getRepoChanges(ctx context.Context, repo *repo_model.Repository, revision s needGenesis := len(status.CommitSha) == 0 if !needGenesis { - hasAncestorCmd := git.NewCommand("merge-base").AddDynamicArguments(status.CommitSha, revision) - stdout, _, _ := hasAncestorCmd.RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}) + hasAncestorCmd := gitcmd.NewCommand("merge-base").AddDynamicArguments(status.CommitSha, revision) + stdout, _, _ := hasAncestorCmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) needGenesis = len(stdout) == 0 } @@ -86,7 +87,7 @@ func parseGitLsTreeOutput(stdout []byte) ([]internal.FileUpdate, error) { // genesisChanges get changes to add repo to the indexer for the first time func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision string) (*internal.RepoChanges, error) { var changes internal.RepoChanges - stdout, _, runErr := git.NewCommand("ls-tree", "--full-tree", "-l", "-r").AddDynamicArguments(revision).RunStdBytes(ctx, &git.RunOpts{Dir: repo.RepoPath()}) + stdout, _, runErr := gitcmd.NewCommand("ls-tree", "--full-tree", "-l", "-r").AddDynamicArguments(revision).RunStdBytes(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) if runErr != nil { return nil, runErr } @@ -98,8 +99,8 @@ func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision s // nonGenesisChanges get changes since the previous indexer update func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revision string) (*internal.RepoChanges, error) { - diffCmd := git.NewCommand("diff", "--name-status").AddDynamicArguments(repo.CodeIndexerStatus.CommitSha, revision) - stdout, _, runErr := diffCmd.RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}) + diffCmd := gitcmd.NewCommand("diff", "--name-status").AddDynamicArguments(repo.CodeIndexerStatus.CommitSha, revision) + stdout, _, runErr := diffCmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) if runErr != nil { // previous commit sha may have been removed by a force push, so // try rebuilding from scratch @@ -115,9 +116,9 @@ func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revisio updatedFilenames := make([]string, 0, 10) updateChanges := func() error { - cmd := git.NewCommand("ls-tree", "--full-tree", "-l").AddDynamicArguments(revision). + cmd := gitcmd.NewCommand("ls-tree", "--full-tree", "-l").AddDynamicArguments(revision). AddDashesAndList(updatedFilenames...) - lsTreeStdout, _, err := cmd.RunStdBytes(ctx, &git.RunOpts{Dir: repo.RepoPath()}) + lsTreeStdout, _, err := cmd.RunStdBytes(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) if err != nil { return err } diff --git a/modules/indexer/code/indexer_test.go b/modules/indexer/code/indexer_test.go index 78fea22f105..a884ab733a8 100644 --- a/modules/indexer/code/indexer_test.go +++ b/modules/indexer/code/indexer_test.go @@ -134,7 +134,7 @@ func testIndexer(name string, t *testing.T, indexer internal.Indexer) { }, }, // Search for matches on both the contents and the filenames within the repo '62'. - // This scenario yields two results: the first result is baed on the file (cucumber.md) while the second is based on the contents + // This scenario yields two results: the first result is based on the file (cucumber.md) while the second is based on the contents { RepoIDs: []int64{62}, Keyword: "cucumber", diff --git a/modules/indexer/issues/indexer.go b/modules/indexer/issues/indexer.go index 8f25c84b760..bbc78aecbe5 100644 --- a/modules/indexer/issues/indexer.go +++ b/modules/indexer/issues/indexer.go @@ -28,7 +28,7 @@ import ( ) // IndexerMetadata is used to send data to the queue, so it contains only the ids. -// It may look weired, because it has to be compatible with the old queue data format. +// It may look weird, because it has to be compatible with the old queue data format. // If the IsDelete flag is true, the IDs specify the issues to delete from the index without querying the database. // If the IsDelete flag is false, the ID specify the issue to index, so Indexer will query the database to get the issue data. // It should be noted that if the id is not existing in the database, it's index will be deleted too even if IsDelete is false. diff --git a/modules/markup/render_link.go b/modules/markup/render_link.go index 046544ce81d..9cc83095ffa 100644 --- a/modules/markup/render_link.go +++ b/modules/markup/render_link.go @@ -18,7 +18,7 @@ func resolveLinkRelative(ctx context.Context, base, cur, link string, absolute b } if strings.HasPrefix(link, "/") { if strings.HasPrefix(link, base) && strings.Count(base, "/") >= 4 { - // a trick to tolerate that some users were using absolut paths (the old gitea's behavior) + // a trick to tolerate that some users were using absolute paths (the old gitea's behavior) finalLink = link } else { finalLink = util.URLJoin(base, "./", link) diff --git a/modules/proxy/proxy.go b/modules/proxy/proxy.go index 1a6bdad7fb5..f8843316aa9 100644 --- a/modules/proxy/proxy.go +++ b/modules/proxy/proxy.go @@ -10,10 +10,9 @@ import ( "strings" "sync" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" - - "github.com/gobwas/glob" ) var ( diff --git a/modules/queue/queue.go b/modules/queue/queue.go index 577fd4d4981..56835014a5a 100644 --- a/modules/queue/queue.go +++ b/modules/queue/queue.go @@ -22,7 +22,7 @@ // // 4. Handler (represented by HandlerFuncT type): // - It's the function responsible for processing items. Each active worker will call it. -// - If an item or some items are not psuccessfully rocessed, the handler could return them as "unhandled items". +// - If an item or some items are not successfully processed, the handler could return them as "unhandled items". // In such scenarios, the queue system ensures these unhandled items are returned to the base queue after a brief delay. // This mechanism is particularly beneficial in cases where the processing entity (like a document indexer) is // temporarily unavailable. It ensures that no item is skipped or lost due to transient failures in the processing diff --git a/modules/setting/glob.go b/modules/setting/glob.go index 8f1d24dea4c..cc76a020771 100644 --- a/modules/setting/glob.go +++ b/modules/setting/glob.go @@ -3,7 +3,7 @@ package setting -import "github.com/gobwas/glob" +import "code.gitea.io/gitea/modules/glob" type GlobMatcher struct { compiledGlob glob.Glob diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 318cf411085..90c4f22ad2e 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -54,6 +54,12 @@ var ( AllowForkWithoutMaximumLimit bool AllowForkIntoSameOwner bool + // StreamArchives makes Gitea stream git archive files to the client directly instead of creating an archive first. + // Ideally all users should use this streaming method. However, at the moment we don't know whether there are + // any users who still need the old behavior, so we introduce this option, intentionally not documenting it. + // After one or two releases, if no one complains, we will remove this option and always use streaming. + StreamArchives bool + // Repository editor settings Editor struct { LineWrapExtensions []string @@ -167,6 +173,7 @@ var ( DisableStars: false, DefaultBranch: "main", AllowForkWithoutMaximumLimit: true, + StreamArchives: true, // Repository editor settings Editor: struct { diff --git a/modules/setting/service.go b/modules/setting/service.go index b1b9fedd62a..e652c13c9c9 100644 --- a/modules/setting/service.go +++ b/modules/setting/service.go @@ -9,10 +9,9 @@ import ( "strings" "time" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/structs" - - "github.com/gobwas/glob" ) // enumerates all the types of captchas diff --git a/modules/setting/service_test.go b/modules/setting/service_test.go index 73736b793a8..fad15427415 100644 --- a/modules/setting/service_test.go +++ b/modules/setting/service_test.go @@ -6,10 +6,10 @@ package setting import ( "testing" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" - "github.com/gobwas/glob" "github.com/stretchr/testify/assert" ) diff --git a/modules/structs/issue.go b/modules/structs/issue.go index 322ac1e4caa..2540481d0ff 100644 --- a/modules/structs/issue.go +++ b/modules/structs/issue.go @@ -17,7 +17,7 @@ import ( type StateType string const ( - // StateOpen pr is opend + // StateOpen pr is opened StateOpen StateType = "open" // StateClosed pr is closed StateClosed StateType = "closed" @@ -76,6 +76,8 @@ type Issue struct { // swagger:strfmt date-time Deadline *time.Time `json:"due_date"` + TimeEstimate int64 `json:"time_estimate"` + PullRequest *PullRequestMeta `json:"pull_request"` Repo *RepositoryMeta `json:"repository"` diff --git a/modules/structs/repo.go b/modules/structs/repo.go index e82a6dc25e9..c1c85837fc8 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -319,7 +319,7 @@ const ( ) // Name represents the service type's name -// WARNNING: the name have to be equal to that on goth's library +// WARNING: the name has to be equal to that on goth's library func (gt GitServiceType) Name() string { return strings.ToLower(gt.Title()) } diff --git a/modules/structs/user.go b/modules/structs/user.go index 89349cda2c7..90dbcff25cb 100644 --- a/modules/structs/user.go +++ b/modules/structs/user.go @@ -61,7 +61,7 @@ type User struct { // MarshalJSON implements the json.Marshaler interface for User, adding field(s) for backward compatibility func (u User) MarshalJSON() ([]byte, error) { - // Re-declaring User to avoid recursion + // Redeclaring User to avoid recursion type shadow User return json.Marshal(struct { shadow diff --git a/modules/typesniffer/typesniffer_test.go b/modules/typesniffer/typesniffer_test.go index a0c824b912e..ad3f78afdc9 100644 --- a/modules/typesniffer/typesniffer_test.go +++ b/modules/typesniffer/typesniffer_test.go @@ -48,12 +48,12 @@ func TestIsSvgImage(t *testing.T) { `)).IsSvgImage()) assert.True(t, DetectContentType([]byte(` - `)).IsSvgImage()) assert.True(t, DetectContentType([]byte(` - `)).IsSvgImage()) diff --git a/modules/validation/binding.go b/modules/validation/binding.go index 75190e31e18..335f248ead1 100644 --- a/modules/validation/binding.go +++ b/modules/validation/binding.go @@ -10,10 +10,10 @@ import ( "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/util" "gitea.com/go-chi/binding" - "github.com/gobwas/glob" ) const ( diff --git a/modules/validation/glob_pattern_test.go b/modules/validation/glob_pattern_test.go index 7f3e609acfa..80cf035ca69 100644 --- a/modules/validation/glob_pattern_test.go +++ b/modules/validation/glob_pattern_test.go @@ -6,8 +6,9 @@ package validation import ( "testing" + "code.gitea.io/gitea/modules/glob" + "gitea.com/go-chi/binding" - "github.com/gobwas/glob" ) func getGlobPatternErrorString(pattern string) string { diff --git a/modules/validation/helpers.go b/modules/validation/helpers.go index ba383ba195d..051330e4397 100644 --- a/modules/validation/helpers.go +++ b/modules/validation/helpers.go @@ -11,9 +11,8 @@ import ( "strings" "sync" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/setting" - - "github.com/gobwas/glob" ) type globalVarsStruct struct { diff --git a/options/fileicon/material-icon-rules.json b/options/fileicon/material-icon-rules.json index 15bbf613db7..622564fe2b7 100644 --- a/options/fileicon/material-icon-rules.json +++ b/options/fileicon/material-icon-rules.json @@ -3367,6 +3367,22 @@ "..claude": "folder-claude", "_.claude": "folder-claude", "__.claude__": "folder-claude", + ".cursor": "folder-cursor", + "..cursor": "folder-cursor", + "_.cursor": "folder-cursor", + "__.cursor__": "folder-cursor", + "metro": "folder-metro", + ".metro": "folder-metro", + "_metro": "folder-metro", + "__metro__": "folder-metro", + "filter": "folder-filter", + ".filter": "folder-filter", + "_filter": "folder-filter", + "__filter__": "folder-filter", + "filters": "folder-filter", + ".filters": "folder-filter", + "_filters": "folder-filter", + "__filters__": "folder-filter", "meta-inf": "folder-config", ".meta-inf": "folder-config", "_meta-inf": "folder-config", @@ -6763,7 +6779,23 @@ ".claude": "folder-claude-open", "..claude": "folder-claude-open", "_.claude": "folder-claude-open", - "__.claude__": "folder-claude-open" + "__.claude__": "folder-claude-open", + ".cursor": "folder-cursor-open", + "..cursor": "folder-cursor-open", + "_.cursor": "folder-cursor-open", + "__.cursor__": "folder-cursor-open", + "metro": "folder-metro-open", + ".metro": "folder-metro-open", + "_metro": "folder-metro-open", + "__metro__": "folder-metro-open", + "filter": "folder-filter-open", + ".filter": "folder-filter-open", + "_filter": "folder-filter-open", + "__filter__": "folder-filter-open", + "filters": "folder-filter-open", + ".filters": "folder-filter-open", + "_filters": "folder-filter-open", + "__filters__": "folder-filter-open" }, "rootFolderNames": {}, "rootFolderNamesExpanded": {}, @@ -6800,6 +6832,7 @@ "sublime-project": "sublime", "sublime-workspace": "sublime", "slx": "simulink", + "qmd": "quarto", "tw": "twine", "twee": "twine", "yml.dist": "yaml", @@ -7366,6 +7399,7 @@ "mli": "ocaml", "cmx": "ocaml", "odin": "odin", + "onnx": "onnx", "js.map": "javascript-map", "mjs.map": "javascript-map", "cjs.map": "javascript-map", @@ -7468,6 +7502,7 @@ "tfvars": "terraform", "tfstate": "terraform", "tfbackend": "terraform", + "tofu": "opentofu", "blade.php": "laravel", "inky.php": "laravel", "applescript": "applescript", @@ -7761,12 +7796,24 @@ "geom": "shader", "frag": "shader", "comp": "shader", + "rgen": "shader", + "rint": "shader", + "rahit": "shader", + "rchit": "shader", + "rmiss": "shader", + "rcall": "shader", "vert.glsl": "shader", "tesc.glsl": "shader", "tese.glsl": "shader", "geom.glsl": "shader", "frag.glsl": "shader", "comp.glsl": "shader", + "rgen.glsl": "shader", + "rint.glsl": "shader", + "rahit.glsl": "shader", + "rchit.glsl": "shader", + "rmiss.glsl": "shader", + "rcall.glsl": "shader", "vertex.glsl": "shader", "geometry.glsl": "shader", "fragment.glsl": "shader", @@ -7790,6 +7837,8 @@ "comp.hlsl": "shader", "tess.hlsl": "shader", "wgsl": "shader", + "spv": "shader", + "slang": "shader", "sy": "siyuan", "ndst.yml": "ndst", "ndst.yaml": "ndst", @@ -7852,6 +7901,7 @@ "hurl": "hurl", "cds": "cds", "slint": "slint", + "verse": "verse", "sw": "sway", "zeabur": "zeabur", "bench.ts": "bench-ts", @@ -9004,6 +9054,7 @@ ".eslintrc.base.json": "eslint", ".eslintignore": "eslint", ".eslintcache": "eslint", + "eslint-options.js": "eslint", "code_of_conduct.md": "conduct", "code_of_conduct.txt": "conduct", "code_of_conduct": "conduct", @@ -9025,6 +9076,7 @@ ".mocharc.json": "mocha", ".mocharc.jsonc": "mocha", "jenkinsfile": "jenkins", + "firebase.config.js": "firebase", "firebase.json": "firebase", ".firebaserc": "firebase", "firestore.rules": "firebase", @@ -10032,6 +10084,18 @@ ".oxlintrc.json": "oxlint", "CLAUDE.md": "claude", "CLAUDE.local.md": "claude", + ".cursorignore": "cursor", + ".cursorindexingignore": "cursor", + ".cursorrules": "cursor", + ".cursor": "cursor", + ".cursor.json": "cursor", + ".cursorrc": "cursor", + "metro.config.js": "metro", + "metro.config.json": "metro", + "src/bashly.yaml": "bashly", + "src/bashly.yml": "bashly", + "bashly-settings.yaml": "bashly-settings", + "bashly-settings.yml": "bashly-settings", "xamlstyler.json": "xaml", ".rhistory": "r", "cmakepresets.json": "cmake", @@ -10113,6 +10177,7 @@ "haml": "haml", "yang": "yang", "terraform": "terraform", + "opentofu": "opentofu", "applescript": "applescript", "cake": "cake", "cucumber": "cucumber", @@ -10243,7 +10308,8 @@ "beancount": "beancount", "ahk2": "ahk2", "gnuplot": "gnuplot", - "helm": "helm" + "helm": "helm", + "nginx": "nginx" }, "light": { "fileExtensions": { @@ -10253,6 +10319,7 @@ "j2": "jinja_light", "jinja-html": "jinja_light", "toml": "toml_light", + "tofu": "opentofu_light", "huff": "huff_light", "cr": "crystal_light", "ecr": "crystal_light", @@ -10275,6 +10342,7 @@ "openapi.yml": "openapi_light", "openapi.yaml": "openapi_light", "tldr": "tldraw_light", + "verse": "verse_light", "zeabur": "zeabur_light" }, "fileNames": { @@ -10437,7 +10505,15 @@ "zeabur.yml": "zeabur_light", "zeabur.toml": "zeabur_light", "copilot-instructions.md": "copilot_light", - "hosts": "hosts_light" + "hosts": "hosts_light", + ".cursorignore": "cursor_light", + ".cursorindexingignore": "cursor_light", + ".cursorrules": "cursor_light", + ".cursor": "cursor_light", + ".cursor.json": "cursor_light", + ".cursorrc": "cursor_light", + "bashly-settings.yaml": "bashly-settings_light", + "bashly-settings.yml": "bashly-settings_light" }, "languageIds": { "toml": "toml_light", @@ -10460,7 +10536,11 @@ "idea": "folder-intellij_light", ".idea": "folder-intellij_light", "_idea": "folder-intellij_light", - "__idea__": "folder-intellij_light" + "__idea__": "folder-intellij_light", + ".cursor": "folder-cursor_light", + "..cursor": "folder-cursor_light", + "_.cursor": "folder-cursor_light", + "__.cursor__": "folder-cursor_light" }, "folderNamesExpanded": { "jinja": "folder-jinja-open_light", @@ -10478,7 +10558,11 @@ "idea": "folder-intellij-open_light", ".idea": "folder-intellij-open_light", "_idea": "folder-intellij-open_light", - "__idea__": "folder-intellij-open_light" + "__idea__": "folder-intellij-open_light", + ".cursor": "folder-cursor-open_light", + "..cursor": "folder-cursor-open_light", + "_.cursor": "folder-cursor-open_light", + "__.cursor__": "folder-cursor-open_light" }, "rootFolderNames": {}, "rootFolderNamesExpanded": {} diff --git a/options/fileicon/material-icon-svgs.json b/options/fileicon/material-icon-svgs.json index 9d1e0ee075f..9aa78359dae 100644 --- a/options/fileicon/material-icon-svgs.json +++ b/options/fileicon/material-icon-svgs.json @@ -49,6 +49,10 @@ "azure": "", "babel": "", "ballerina": "", + "bashly-hook": "", + "bashly-settings.clone": "", + "bashly-settings_light.clone": "", + "bashly": "", "bazel": "", "bbx": "", "beancount": "", @@ -116,7 +120,7 @@ "commitlint": "", "concourse": "", "conduct": "", - "console": "", + "console": "", "container.clone": "", "contentlayer": "", "context": "", @@ -134,6 +138,8 @@ "css": "", "cucumber": "", "cuda": "", + "cursor": "", + "cursor_light": "", "cypress": "", "d": "", "dart": "", @@ -287,6 +293,10 @@ "folder-coverage": "", "folder-css-open": "", "folder-css": "", + "folder-cursor-open": "", + "folder-cursor-open_light": "", + "folder-cursor": "", + "folder-cursor_light": "", "folder-custom-open": "", "folder-custom": "", "folder-cypress-open": "", @@ -339,6 +349,8 @@ "folder-fastlane": "", "folder-favicon-open": "", "folder-favicon": "", + "folder-filter-open": "", + "folder-filter": "", "folder-firebase-open": "", "folder-firebase": "", "folder-firestore-open": "", @@ -463,6 +475,8 @@ "folder-messages": "", "folder-meta-open": "", "folder-meta": "", + "folder-metro-open": "", + "folder-metro": "", "folder-middleware-open": "", "folder-middleware": "", "folder-mjml-open": "", @@ -830,6 +844,7 @@ "merlin": "", "mermaid": "", "meson": "", + "metro": "", "minecraft-fabric": "", "minecraft": "", "mint": "", @@ -881,10 +896,13 @@ "objective-cpp": "", "ocaml": "", "odin": "", + "onnx": "", "opa": "", "opam": "", "openapi": "", "openapi_light": "", + "opentofu": "", + "opentofu_light": "", "otne": "", "oxlint": "", "packship": "", @@ -935,6 +953,7 @@ "python": "", "pytorch": "", "qsharp": "", + "quarto": "", "quasar": "", "quokka": "", "qwik": "", @@ -1100,6 +1119,8 @@ "verdaccio": "", "verified": "", "verilog": "", + "verse": "", + "verse_light": "", "vfl": "", "video": "", "vim": "", diff --git a/options/gitignore/Gradle b/options/gitignore/Gradle index a5b111377bf..f93fdf44427 100644 --- a/options/gitignore/Gradle +++ b/options/gitignore/Gradle @@ -8,7 +8,7 @@ gradle-app.setting # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) !gradle-wrapper.jar -# Avoid ignore Gradle wrappper properties +# Avoid ignore Gradle wrapper properties !gradle-wrapper.properties # Cache of project diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index ffac68e262d..cc92f2e626d 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -3425,7 +3425,6 @@ swift.install=Přidejte balíček do svého Package.swift souboru: swift.install2=a spustit následující příkaz: vagrant.install=Pro přidání Vagrant box spusťte následující příkaz: settings.link=Propojit tento balíček s repozitářem -settings.link.description=Pokud propojíte balíček s repozitářem, je tento balíček uveden v seznamu balíčků repozitáře. settings.link.select=Vybrat repozitář settings.link.button=Aktualizovat odkaz na repozitář settings.link.success=Odkaz na repozitář byl úspěšně aktualizován. diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 8c0731edd9b..dae7275f82e 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -3475,7 +3475,6 @@ swift.install=Füge das Paket deiner Package.swift Datei hinzu: swift.install2=und führe den folgenden Befehl aus: vagrant.install=Um eine Vagrant-Box hinzuzufügen, führe den folgenden Befehl aus: settings.link=Dieses Paket einem Repository zuweisen -settings.link.description=Wenn du ein Paket mit einem Repository verknüpfst, wird es in der Paketliste des Repositories angezeigt. settings.link.select=Repository auswählen settings.link.button=Repository-Link aktualisieren settings.link.success=Repository-Link wurde erfolgreich aktualisiert. diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 60876b1ab00..42b23dea058 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -3133,7 +3133,6 @@ swift.install=Προσθέστε το πακέτο στο αρχείο Pac swift.install2=και εκτελέστε την ακόλουθη εντολή: vagrant.install=Για προσθήκη ενός κυτίου Vagrant, εκτελέστε την ακόλουθη εντολή: settings.link=Σύνδεση αυτού του πακέτου με ένα αποθετήριο -settings.link.description=Εάν συνδέσετε ένα πακέτο με ένα αποθετήριο, το πακέτο περιλαμβάνεται στη λίστα πακέτων του αποθετηρίου. settings.link.select=Επιλογή Αποθετηρίου settings.link.button=Ενημέρωση Συνδέσμου Αποθετηρίου settings.link.success=Ο σύνδεσμος αποθετηρίου ενημερώθηκε επιτυχώς. diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index a974b172256..b3eb7b1f4a4 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3729,11 +3729,14 @@ swift.install = Add the package in your Package.swift file: swift.install2 = and run the following command: vagrant.install = To add a Vagrant box, run the following command: settings.link = Link this package to a repository -settings.link.description = If you link a package with a repository, the package is listed in the repository's package list. +settings.link.description = If you link a package with a repository, the package will appear in the repository's package list. Only repositories under the same owner can be linked. Leaving the field empty will remove the link. settings.link.select = Select Repository settings.link.button = Update Repository Link settings.link.success = Repository link was successfully updated. settings.link.error = Failed to update repository link. +settings.link.repo_not_found = Repository %s not found. +settings.unlink.error = Failed to remove repository link. +settings.unlink.success = Repository link was successfully removed. settings.delete = Delete package settings.delete.description = Deleting a package is permanent and cannot be undone. settings.delete.notice = You are about to delete %s (%s). This operation is irreversible, are you sure? diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index 882a06330af..ffacb46b199 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -3113,7 +3113,6 @@ swift.install=Añade el paquete en tu archivo Package.swift: swift.install2=y ejecuta el siguiente comando: vagrant.install=Para añadir un paquete Vagrant, ejecuta el siguiente comando: settings.link=Vincular este paquete a un repositorio -settings.link.description=Si enlaza un paquete con un repositorio, el paquete se enumera en la lista de paquetes del repositorio. settings.link.select=Seleccionar repositorio settings.link.button=Actualizar enlace de repositorio settings.link.success=El enlace del repositorio se ha actualizado correctamente. diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index f75976bc4f0..5814de64ca8 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -120,6 +120,7 @@ error404=La page que vous essayez d'atteindre n'existe pas ou < error503=Le serveur n’a pas pu répondre à votre demande. Veuillez réessayer plus tard. go_back=Retour invalid_data=Données invalides : %v +nothing_has_been_changed=Rien n’a été changé. never=Jamais unknown=Inconnu @@ -550,6 +551,13 @@ repo.transfer.body=Pour l’accepter ou le rejeter, visitez %s ou ignorez-le. repo.collaborator.added.subject=%s vous a ajouté à %s repo.collaborator.added.text=Vous avez été ajouté en tant que collaborateur du dépôt : +repo.actions.run.failed=L’exécution a échoué +repo.actions.run.succeeded=L’exécution a réussi +repo.actions.run.cancelled=L’exécution a été annulée +repo.actions.jobs.all_succeeded=Tous les tâches ont réussi. +repo.actions.jobs.all_failed=Toutes les tâches ont échoué. +repo.actions.jobs.some_not_successful=Certaines tâches n’ont pas réussi. +repo.actions.jobs.all_cancelled=Toutes les tâches ont bien été annulés. team_invite.subject=%[1]s vous a invité à rejoindre l’organisation %[2]s team_invite.text_1=%[1]s vous a invité à rejoindre l’équipe %[2]s dans l’organisation %[3]s. @@ -2844,6 +2852,11 @@ settings.location=Localisation settings.permission=Autorisations settings.repoadminchangeteam=L'administrateur de dépôt peut ajouter et supprimer l'accès aux équipes settings.visibility=Visibilité +settings.change_visibility=Modifier la visibilité +settings.change_visibility_notices_1=Si l’organisation est convertie en privé, les étoiles du dépôt seront supprimées et ne pourront pas être restaurées. +settings.change_visibility_notices_2=Les non-membres ne pourront plus accéder aux dépôts de l’organisation si la visibilité devient privée. +settings.change_visibility_success=La visibilité de l’organisation %s a été modifiée. +settings.visibility_desc=Changez qui peut voir l’organisation et ses dépôts. settings.visibility.public=Public settings.visibility.limited=Limité (Visible uniquement aux utilisateurs authentifiés) settings.visibility.limited_shortname=Limité @@ -3420,6 +3433,7 @@ config.picture_service=Service d'Imagerie config.disable_gravatar=Désactiver Gravatar config.enable_federated_avatar=Activer les avatars unifiés config.open_with_editor_app_help=Les éditeurs disponibles via « Ouvrir avec ». Si laissé vide, la valeur par défaut sera utilisée. Développez pour voir la valeur par défaut. +config.git_guide_remote_name=Nom du dépôt distant pour les commandes git dans le guide config.git_config=Configuration de Git config.git_disable_diff_highlight=Désactiver la surbrillance syntaxique de Diff @@ -3715,7 +3729,6 @@ swift.install=Ajoutez le paquet dans votre fichier Package.swift: swift.install2=et exécutez la commande suivante : vagrant.install=Pour ajouter une machine Vagrant, exécutez la commande suivante : settings.link=Lier ce paquet à un dépôt -settings.link.description=Si vous liez un paquet à dépôt, le paquet sera inclus dans sa liste des paquets. settings.link.select=Sélectionner un dépôt settings.link.button=Actualiser le lien du dépôt settings.link.success=Le lien du dépôt a été mis à jour avec succès. diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini index ae8068ea10e..b5b05209e5a 100644 --- a/options/locale/locale_ga-IE.ini +++ b/options/locale/locale_ga-IE.ini @@ -3729,11 +3729,14 @@ swift.install=Cuir an pacáiste i do chomhad Package.swift: swift.install2=agus reáchtáil an t-ordú seo a leanas: vagrant.install=Chun bosca Vagrant a chur leis, reáchtáil an t-ordú seo a leanas: settings.link=Nasc an pacáiste seo le stóras -settings.link.description=Má nascann tú pacáiste le stóras, liostaítear an pacáiste i liosta pacáistí an stórais. +settings.link.description=Má nascann tú pacáiste le stórlann, beidh an pacáiste le feiceáil i liosta pacáistí an stórlainne. Ní féidir ach stórlanna faoin úinéir céanna a nascadh. Má fhágtar an réimse folamh, bainfear an nasc. settings.link.select=Roghnaigh Stóras settings.link.button=Nuashonraigh Nasc Stórais settings.link.success=D'éirigh le nasc an stórais a nuashonrú. settings.link.error=Theip ar an nasc stóras a nuashonrú. +settings.link.repo_not_found=Níor aimsíodh an stóras %s. +settings.unlink.error=Theip ar nasc an stórais a bhaint. +settings.unlink.success=Baineadh an nasc chuig an stóras go rathúil. settings.delete=Scrios pacáiste settings.delete.description=Tá pacáiste a scriosadh buan agus ní féidir é a chur ar ais. settings.delete.notice=Tá tú ar tí %s (%s) a scriosadh. Tá an oibríocht seo dochúlaithe, an bhfuil tú cinnte? diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 2519688d086..31aebff2d61 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -2667,7 +2667,6 @@ rubygems.dependencies.development=Dipendenze Di Sviluppo rubygems.required.ruby=Richiede la versione di Ruby rubygems.required.rubygems=Richiede la versione RubyGem settings.link=Collega questo pacchetto a un repository -settings.link.description=Se si collega un pacchetto a un repository, il pacchetto è elencato nell'elenco dei pacchetti del repository. settings.link.select=Seleziona Repository settings.link.button=Aggiorna Collegamento Repository settings.link.success=Il link del repository è stato aggiornato correttamente. diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 14a4825c40c..3b3083d8c68 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -3722,7 +3722,6 @@ swift.install=あなたの Package.swift ファイルにパッケ swift.install2=そして次のコマンドを実行します: vagrant.install=Vagrant ボックスを追加するには、次のコマンドを実行します。 settings.link=このパッケージをリポジトリにリンク -settings.link.description=パッケージをリポジトリにリンクすると、リポジトリのパッケージリストに表示されるようになります。 settings.link.select=リポジトリを選択 settings.link.button=リポジトリのリンクを更新 settings.link.success=リポジトリのリンクが正常に更新されました。 diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 0ec540104f1..9c8f47534bf 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -3134,7 +3134,6 @@ swift.install=Pievienojiet pakotni savā Package.swift failā: swift.install2=un izpildiet sekojošu komandu: vagrant.install=Lai pievienotu Vagrant kasti, izpildiet sekojošu komandu: settings.link=Piesaistīt pakotni šim repozitorijam -settings.link.description=Sasaistot pakotni ar repozitoriju, tā tiks attēlota repozitorija pakotņu sarakstā. settings.link.select=Norādiet repozitoriju settings.link.button=Atjaunot repozitorija saiti settings.link.success=Repozitorija saite tika veiksmīgi atjaunota. diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index e345c336b40..b457ecc8004 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -1010,9 +1010,9 @@ visibility.private_tooltip=Visível apenas para membros das organizações às q new_repo_helper=Um repositório contém todos os arquivos do projeto, inclusive o histórico de revisões. Já está hospedando um em outro lugar? Migre o repositório. owner=Proprietário owner_helper=Algumas organizações podem não aparecer no menu devido a um limite de contagem dos repositórios. -repo_name=Nome do repositório +repo_name=Nome do Repositório repo_name_helper=Bons nomes de repositórios usam palavras-chave curtas, memorizáveis e únicas. Um repositório chamado ".profile" ou ".profile-private" pode ser usado para adicionar um README.md para seu perfil de usuário/organização. -repo_size=Tamanho do repositório +repo_size=Tamanho do Repositório template=Modelo template_select=Selecione um modelo. template_helper=Tornar repositório um modelo @@ -1079,7 +1079,7 @@ mirror_lfs=Armazenamento de Arquivo Grande (LFS) mirror_lfs_desc=Ativar espelhamento de dados LFS. mirror_lfs_endpoint=Destino LFS mirror_lfs_endpoint_desc=A sincronização tentará usar o URL de clonagem para determinar o servidor LFS. Você também pode especificar um destino personalizado se os dados do repositório LFS forem armazenados em outro lugar. -mirror_last_synced=Última sincronização +mirror_last_synced=Última Sincronização mirror_password_placeholder=(inalterada) mirror_password_blank_placeholder=(não definida) mirror_password_help=Altere o nome de usuário para apagar uma senha armazenada. @@ -1103,9 +1103,9 @@ delete_preexisting_success=Arquivos órfãos excluídos em %s blame_prior=Ver a responsabilização anterior a esta modificação -transfer.accept=Aceitar transferência +transfer.accept=Aceitar Transferência transfer.accept_desc=`Transferir para "%s"` -transfer.reject=Rejeitar transferência +transfer.reject=Rejeitar Transferência transfer.reject_desc=`Cancelar a transferência para "%s"` transfer.no_permission_to_accept=Você não tem permissão para aceitar essa transferência. transfer.no_permission_to_reject=Você não tem permissão para rejeitar essa transferência. @@ -1118,14 +1118,14 @@ desc.internal=Interno desc.archived=Arquivado desc.sha256=SHA256 -template.items=Itens do modelo +template.items=Itens do Modelo template.git_content=Conteúdo Git (Branch padrão) template.git_hooks=Hooks do Git template.git_hooks_tooltip=Atualmente você não pode modificar ou remover os Git Hooks adicionados. Selecione isso apenas se você confia no repositório modelo. template.webhooks=Webhooks template.topics=Tópicos template.avatar=Avatar -template.issue_labels=Etiquetas de issue +template.issue_labels=Etiquetas de Issue template.one_item=Deve-se selecionar pelo menos um item de modelo template.invalid=Deve-se selecionar um repositório de modelo @@ -1148,7 +1148,7 @@ migrate_options_lfs_endpoint.label=Destino LFS migrate_options_lfs_endpoint.description=A migração tentará usar seu controle remoto Git para determinar o servidor LFS. Você também pode especificar um destino personalizado se os dados do repositório LFS forem armazenados em outro lugar. migrate_options_lfs_endpoint.description.local=Um caminho de servidor local também é suportado. migrate_options_lfs_endpoint.placeholder=Se for deixado em branco, o destino será derivado do URL de clone. -migrate_items=Itens da migração +migrate_items=Itens da Migração migrate_items_wiki=Wiki migrate_items_milestones=Marcos migrate_items_labels=Etiquetas @@ -1232,7 +1232,7 @@ find_tag=Pesquisar etiqueta branches=Branches tags=Tags issues=Issues -pulls=Pull requests +pulls=Pull Requests projects=Projetos packages=Pacotes actions=Ações @@ -1363,7 +1363,7 @@ commits.no_commits=Nenhum commit em comum. "%s" e "%s" tem históricos completam commits.nothing_to_compare=Estes branches são iguais. commits.search.tooltip=Você pode prefixar as palavras-chave com "author:" (autor da mudança), "committer:" (autor do commit), "after:" (depois) ou "before:" (antes). Por exemplo: "revert author:Ana before:2019-01-13".\ commits.search_branch=Este Branch -commits.search_all=Todos os branches +commits.search_all=Todos os Branches commits.author=Autor commits.message=Mensagem commits.date=Data @@ -1396,7 +1396,7 @@ projects.description=Descrição (opcional) projects.description_placeholder=Descrição projects.create=Criar Projeto projects.title=Título -projects.new=Novo projeto +projects.new=Novo Projeto projects.new_subheader=Coordene, acompanhe e atualize seu trabalho em um só lugar, para que os projetos permaneçam transparentes e dentro do cronograma. projects.create_success=Projeto "%s" criado. projects.deletion=Apagar Projeto @@ -1407,25 +1407,25 @@ projects.edit_subheader=Projetos organizam issues e acompanham o progresso. projects.modify=Atualizar Projeto projects.edit_success=Projeto "%s" atualizado. projects.type.none=Nenhum -projects.type.basic_kanban=Kanban básico +projects.type.basic_kanban=Kanban Básico projects.type.bug_triage=Triagem de Bugs projects.template.desc=Modelo de projeto projects.template.desc_helper=Selecione um modelo de projeto para começar projects.column.edit=Editar coluna projects.column.edit_title=Nome projects.column.new_title=Nome -projects.column.new_submit=Criar coluna +projects.column.new_submit=Criar Coluna projects.column.new=Nova Coluna -projects.column.set_default=Atribuir como padrão +projects.column.set_default=Atribuir como Padrão projects.column.set_default_desc=Definir esta coluna como padrão para pull e issues sem categoria -projects.column.delete=Excluir coluna +projects.column.delete=Excluir Coluna projects.column.color=Cor projects.open=Abrir projects.close=Fechar projects.column.assigned_to=Atribuído a projects.card_type.desc=Pré-visualizações de Cards projects.card_type.images_and_text=Imagens e Texto -projects.card_type.text_only=Somente texto +projects.card_type.text_only=Somente Texto issues.desc=Organize relatórios de bugs, tarefas e marcos. issues.filter_assignees=Filtrar Atribuição @@ -1665,7 +1665,7 @@ issues.add_time_hours=Horas issues.add_time_minutes=Minutos issues.add_time_sum_to_small=Nenhum tempo foi inserido. issues.time_spent_total=Tempo Total Gasto -issues.time_spent_from_all_authors=`Tempo total gasto: %s` +issues.time_spent_from_all_authors=`Tempo Total Gasto: %s` issues.due_date=Data Limite issues.invalid_due_date_format=Formato da data limite inválido, deve ser 'dd/mm/aaaa'. @@ -1755,9 +1755,9 @@ compare.compare_base=base compare.compare_head=comparar pulls.desc=Habilitar pull requests e revisões de código. -pulls.new=Novo pull request +pulls.new=Novo Pull Request pulls.view=Ver Pull Request -pulls.compare_changes=Novo pull request +pulls.compare_changes=Novo Pull Request pulls.allow_edits_from_maintainers=Permitir edições de mantenedores pulls.allow_edits_from_maintainers_desc=Usuários com acesso de gravação para o branch base também podem fazer push para este branch pulls.allow_edits_from_maintainers_err=Falha na atualização @@ -1787,7 +1787,7 @@ pulls.merged_title_desc=aplicou merge dos %[1]d commits de %[2]s em pulls.change_target_branch_at=`mudou o branch de destino de %s para %s %s` pulls.tab_conversation=Conversação pulls.tab_commits=Commits -pulls.tab_files=Arquivos alterados +pulls.tab_files=Arquivos Alterados pulls.reopen_to_merge=Por favor reabra este pull request para aplicar o merge. pulls.cant_reopen_deleted_branch=Este pull request não pode ser reaberto porque o branch foi excluído. pulls.merged=Merge aplicado @@ -1837,9 +1837,9 @@ pulls.merge_commit_id=A ID de merge commit pulls.require_signed_wont_sign=O branch requer commits assinados, mas este merge não será assinado pulls.invalid_merge_option=Você não pode usar esta opção de merge neste pull request. -pulls.merge_conflict_summary=Mensagem de erro +pulls.merge_conflict_summary=Mensagem de Erro pulls.rebase_conflict_summary=Mensagem de Erro -pulls.push_rejected_summary=Mensagem completa da rejeição +pulls.push_rejected_summary=Mensagem Completa da Rejeição pulls.open_unmerged_pull_exists=`Não é possível executar uma operação de reabertura pois há um pull request pendente (#%d) com propriedades idênticas.` pulls.status_checking=Algumas verificações estão pendentes pulls.status_checks_success=Todas as verificações foram bem sucedidas @@ -1855,7 +1855,7 @@ pulls.update_branch_rebase=Atualizar branch por rebase pulls.update_branch_success=Atualização do branch foi bem-sucedida pulls.update_not_allowed=Você não tem permissão para atualizar o branch pulls.outdated_with_base_branch=Este branch está desatualizado com o branch base -pulls.close=Fechar pull request +pulls.close=Fechar Pull Request pulls.closed_at=`fechou este pull request %[2]s` pulls.reopened_at=`reabriu este pull request %[2]s` pulls.cmd_instruction_checkout_title=Checkout @@ -1885,7 +1885,7 @@ pull.deleted_branch=(excluído):%s pull.agit_documentation=Veja a documentação sobre AGit -milestones.new=Novo marco +milestones.new=Novo Marco milestones.closed=Fechado %s milestones.update_ago=Atualizado há %s milestones.no_due_date=Sem data limite @@ -1896,11 +1896,11 @@ milestones.completeness=%d%% Concluído milestones.create=Criar Marco milestones.title=Título milestones.desc=Descrição -milestones.due_date=Data limite (opcional) +milestones.due_date=Data Limite (opcional) milestones.clear=Limpar milestones.invalid_due_date_format=Formato da data limite deve ser 'dd/mm/aaaa'. milestones.create_success=O marco "%s" foi criado. -milestones.edit=Editar marco +milestones.edit=Editar Marco milestones.edit_subheader=Marcos organizam as issues e acompanham o progresso. milestones.cancel=Cancelar milestones.modify=Atualizar Marco @@ -1931,24 +1931,24 @@ ext_wiki=Acesso a Wiki Externo ext_wiki.desc=Link para uma wiki externa. wiki=Wiki -wiki.welcome=Bem-vindo a wiki. +wiki.welcome=Bem-vindo a Wiki. wiki.welcome_desc=A wiki permite que você escreva e compartilhe a documentação com os colaboradores. wiki.desc=Escrever e compartilhar a documentação com os colaboradores. -wiki.create_first_page=Criar a primeira página +wiki.create_first_page=Criar a Primeira Página wiki.page=Página wiki.filter_page=Filtrar página wiki.new_page=Página wiki.page_title=Título da página wiki.page_content=Conteúdo wiki.default_commit_message=Escreva uma nota sobre a atualização nesta página (opcional). -wiki.save_page=Salvar página +wiki.save_page=Salvar Página wiki.last_commit_info=%s editou esta página %s wiki.edit_page_button=Editar -wiki.new_page_button=Nova página -wiki.file_revision=Revisão de página +wiki.new_page_button=Nova Página +wiki.file_revision=Revisão de Página wiki.wiki_page_revisions=Revisões de página Wiki wiki.back_to_wiki=Voltar para página Wiki -wiki.delete_page_button=Excluir página +wiki.delete_page_button=Excluir Página wiki.delete_page_notice_1=A exclusão da página de wiki "%s" não pode ser desfeita. Continuar? wiki.page_already_exists=Uma página de wiki com o mesmo nome já existe. wiki.reserved_page=O nome da página da wiki "%s" está reservado. @@ -2042,7 +2042,7 @@ settings.collaboration.undefined=Indefinido settings.hooks=Webhooks settings.githooks=Hooks do Git settings.basic_settings=Configurações Básicas -settings.mirror_settings=Opções de espelhamento +settings.mirror_settings=Opções de Espelhamento settings.mirror_settings.docs=Configure o seu repositório para sincronizar automaticamente commits, tags e branches de outro repositório. settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning=No momento, isso só pode ser feito no menu "Nova migração". Para obter mais informações, consulte: settings.mirror_settings.docs.can_still_use=Embora não seja possível modificar os espelhos existentes ou criar novos espelhos, você ainda pode usar o espelho existente. @@ -2057,25 +2057,25 @@ settings.mirror_settings.push_mirror.remote_url=URL do repositório do Git remot settings.mirror_settings.push_mirror.add=Adicionar Espelho de Push settings.mirror_settings.push_mirror.edit_sync_time=Editar intervalo de sincronização de espelhos -settings.sync_mirror=Sincronizar agora +settings.sync_mirror=Sincronizar Agora settings.site=Site -settings.update_settings=Atualizar configurações -settings.update_mirror_settings=Atualizar espelho -settings.branches.switch_default_branch=Alterar +settings.update_settings=Atualizar Configurações +settings.update_mirror_settings=Atualizar Configurações de Espelhamento +settings.branches.switch_default_branch=Alterar Branch Padrão settings.branches.update_default_branch=Atualizar Branch Padrão settings.branches.add_new_rule=Adicionar Nova Regra -settings.advanced_settings=Configurações avançadas -settings.wiki_desc=Habilitar a wiki do repositório -settings.use_internal_wiki=Usar a wiki nativa +settings.advanced_settings=Configurações Avançadas +settings.wiki_desc=Habilitar a Wiki do Repositório +settings.use_internal_wiki=Usar a Wiki Nativa settings.default_wiki_branch_name=Nome Padrão do Branch da Wiki -settings.use_external_wiki=Usar wiki externa -settings.external_wiki_url=URL externa da wiki +settings.use_external_wiki=Usar Wiki Externa +settings.external_wiki_url=URL Externa da Wiki settings.external_wiki_url_error=A URL da wiki externa não é válida. settings.external_wiki_url_desc=Visitantes são redirecionados para a URL da wiki externa ao clicar na aba da wiki. settings.issues_desc=Habilitar Issue Tracker para o Repositório settings.use_internal_issue_tracker=Usar o issue tracker nativo settings.use_external_issue_tracker=Usar issue tracker externo -settings.external_tracker_url=URL do issue tracker externo +settings.external_tracker_url=URL do Issue Tracker Externo settings.external_tracker_url_error=A URL do issue tracker externo não é válida. settings.external_tracker_url_desc=Visitantes são redirecionados para a URL do issue tracker externo ao clicar na aba de issues. settings.tracker_url_format=Formato de URL do issue tracker externo @@ -2099,29 +2099,29 @@ settings.releases_desc=Habilitar versões do Repositório settings.packages_desc=Habilitar Registro de Pacotes de Repositório settings.projects_desc=Habilitar Projetos settings.projects_mode_all=Todos os projetos -settings.actions_desc=Habilitar ações do repositório -settings.admin_settings=Configurações do administrador +settings.actions_desc=Habilitar Ações do Repositório +settings.admin_settings=Configurações do Administrador settings.admin_enable_health_check=Habilitar verificações de integridade (git fsck) no repositório -settings.admin_code_indexer=Indexador de código +settings.admin_code_indexer=Indexador de Código settings.admin_stats_indexer=Indexador de Estatísticas do Código settings.admin_indexer_commit_sha=Último SHA indexado settings.admin_indexer_unindexed=Não indexado settings.reindex_button=Adicionar à fila de reindexação settings.reindex_requested=Reindexação Requisitada settings.admin_enable_close_issues_via_commit_in_any_branch=Fechar issue via commit em um branch não padrão -settings.danger_zone=Zona de perigo +settings.danger_zone=Zona de Perigo settings.new_owner_has_same_repo=O novo proprietário já tem um repositório com o mesmo nome. Por favor, escolha outro nome. settings.convert=Converter para repositório tradicional settings.convert_desc=Você pode converter este espelhamento em um repositório tradicional. Esta ação não pode ser revertida. settings.convert_notices_1=Esta operação vai converter este espelhamento em um repositório tradicional. Esta ação não pode ser desfeita. -settings.convert_confirm=Converter o repositório +settings.convert_confirm=Converter o Repositório settings.convert_succeed=O espelhamento foi convertido em um repositório tradicional. settings.convert_fork=Converter Para Um Repositório Normal settings.convert_fork_desc=Você pode converter este fork em um repositório normal. Esta ação não pode ser desfeita. settings.convert_fork_notices_1=Esta operação irá converter o fork em um repositório normal e não pode ser desfeita. -settings.convert_fork_confirm=Converter repositório +settings.convert_fork_confirm=Converter Repositório settings.convert_fork_succeed=O fork foi convertido em um repositório normal. -settings.transfer=Transferir propriedade +settings.transfer=Transferir Propriedade settings.transfer.rejected=A transferência do repositório foi rejeitada. settings.transfer.success=A transferência do repositório foi bem sucedida. settings.transfer_abort=Cancelar transferência @@ -2132,7 +2132,7 @@ settings.transfer_form_title=Digite o nome do repositório para confirmar: settings.transfer_notices_1=- Você perderá o acesso ao repositório se transferir para um usuário individual. settings.transfer_notices_2=- Você manterá acesso ao repositório se transferi-lo para uma organização que você também é proprietário. settings.transfer_notices_3=- Se o repositório for privado e for transferido para um usuário individual, esta ação certifica que o usuário tem pelo menos permissão de leitura (e altera as permissões se necessário). -settings.transfer_owner=Novo proprietário +settings.transfer_owner=Novo Proprietário settings.transfer_perform=Executar Transferência settings.transfer_started=`Este repositório foi marcado para transferência e aguarda a confirmação de "%s"` settings.transfer_succeed=O repositório foi transferido. @@ -2145,7 +2145,7 @@ settings.trust_model.collaborator.long=Colaborador: Confiar em assinaturas feita settings.trust_model.committer=Committer settings.trust_model.collaboratorcommitter=Colaborador+Commiter settings.trust_model.collaboratorcommitter.long=Colaborador+Committer: Confiar na assinatura dos colaboradores que correspondem ao autor do commit -settings.wiki_delete=Excluir dados da wiki +settings.wiki_delete=Excluir Dados da Wiki settings.wiki_delete_desc=A exclusão de dados da wiki é permanente e não pode ser desfeita. settings.wiki_delete_notices_1=- Isso excluirá e desabilitará permanentemente a wiki do repositório %s. settings.confirm_wiki_delete=Excluir Dados da Wiki @@ -2156,14 +2156,14 @@ settings.delete_notices_1=- Esta operação NÃO PODERÁ ser de settings.delete_notices_fork_1=- Forks deste repositório se tornarão independentes após a exclusão. settings.deletion_success=O repositório foi excluído. settings.update_settings_success=As configurações do repositório foram atualizadas. -settings.confirm_delete=Excluir repositório -settings.add_collaborator=Adicionar colaborador +settings.confirm_delete=Excluir Repositório +settings.add_collaborator=Adicionar Colaborador settings.add_collaborator_success=O colaborador foi adicionado. settings.add_collaborator_inactive_user=Não é possível adicionar um usuário inativo como colaborador. settings.add_collaborator_owner=Não é possível adicionar um proprietário como um colaborador. settings.add_collaborator_duplicate=O colaborador já está adicionado a este repositório. settings.delete_collaborator=Remover -settings.collaborator_deletion=Remover colaborador +settings.collaborator_deletion=Remover Colaborador settings.collaborator_deletion_desc=A exclusão de um colaborador irá revogar o acesso a este repositório. Continuar? settings.remove_collaborator_success=O colaborador foi removido. settings.org_not_allowed_to_be_collaborator=Organizações não podem ser adicionadas como um colaborador. @@ -2174,10 +2174,10 @@ settings.add_team=Adicionar Equipe settings.add_team_duplicate=A equipe já tem o repositório settings.delete_team_tip=Esta equipe tem acesso a todos os repositórios e não pode ser removida settings.remove_team_success=O acesso da equipe ao repositório foi removido. -settings.add_webhook=Adicionar webhook +settings.add_webhook=Adicionar Webhook settings.add_webhook.invalid_channel_name=Nome do canal no webhook não pode estar em branco e não pode conter somente o caractere #. settings.hooks_desc=Webhooks automaticamente fazem requisições de HTTP POST para um servidor quando acionados determinados eventos de Gitea. Leia mais no guia de webhooks. -settings.webhook_deletion=Remover webhook +settings.webhook_deletion=Remover Webhook settings.webhook_deletion_desc=A exclusão de um webhook exclui suas configurações e o histórico de entrega. Continuar? settings.webhook_deletion_success=O webhook foi removido. settings.webhook.test_delivery=Testar Evento de Push @@ -2194,7 +2194,7 @@ settings.githook_name=Nome do Hook settings.githook_content=Conteúdo do Hook settings.update_githook=Atualizar Hook settings.add_webhook_desc=Gitea enviará requisições POST com um tipo de conteúdo especificado para a URL de destino. Leia mais no guia de webhooks. -settings.payload_url=URL de destino +settings.payload_url=URL de Destino settings.http_method=Método HTTP settings.content_type=Tipo de conteúdo POST settings.secret=Senha @@ -2204,9 +2204,9 @@ settings.slack_color=Cor settings.discord_username=Nome de usuário settings.discord_icon_url=URL do ícone settings.event_desc=Acionado em: -settings.event_push_only=Eventos de push -settings.event_send_everything=Todos os eventos -settings.event_choose=Eventos personalizados... +settings.event_push_only=Eventos de Push +settings.event_send_everything=Todos os Eventos +settings.event_choose=Eventos Personalizados… settings.event_header_repository=Eventos do Repositório settings.event_create=Criar settings.event_create_desc=Branch ou tag criado. @@ -2232,10 +2232,10 @@ settings.event_issue_label=Issue Rotulada settings.event_issue_label_desc=Rótulos da issue atualizados ou removidos. settings.event_issue_milestone=Marco Atribuído à Issue settings.event_issue_milestone_desc=Marco atribuído ou desatribuído à Issue. -settings.event_issue_comment=Comentário da issue +settings.event_issue_comment=Comentário da Issue settings.event_issue_comment_desc=Comentário da issue criado, editado ou excluído. settings.event_header_pull_request=Eventos de Pull Request -settings.event_pull_request=Pull request +settings.event_pull_request=Pull Request settings.event_pull_request_desc=Pull request aberto, fechado, reaberto, editado ou excluído. settings.event_pull_request_assign=Pull Request Atribuído settings.event_pull_request_assign_desc=Pull request atribuído ou desatribuído. @@ -2257,9 +2257,9 @@ settings.authorization_header_desc=Será incluído como header de autorização settings.active=Ativo settings.active_helper=Informações sobre eventos disparados serão enviadas para esta URL do webhook. settings.add_hook_success=O webhook foi adicionado. -settings.update_webhook=Atualizar webhook +settings.update_webhook=Atualizar Webhook settings.update_hook_success=O webhook foi atualizado. -settings.delete_webhook=Remover webhook +settings.delete_webhook=Remover Webhook settings.recent_deliveries=Entregas Recentes settings.hook_type=Tipo de Hook settings.slack_token=Token @@ -2285,14 +2285,14 @@ settings.packagist_package_url=URL do pacote do Packagist settings.deploy_keys=Chaves de Deploy settings.add_deploy_key=Nova Chave de Deploy settings.deploy_key_desc=As chaves de deploy possuem somente acesso de leitura (pull) ao repositório. -settings.is_writable=Habilitar acesso de escrita +settings.is_writable=Habilitar Acesso de Escrita settings.is_writable_info=Permitir que esta chave de deploy faça push para o repositório. settings.no_deploy_keys=Não há nenhuma chave de deploy ainda. settings.title=Título settings.deploy_key_content=Conteúdo da chave settings.key_been_used=Uma chave de deploy com conteúdo idêntico já está em uso. settings.key_name_used=Uma chave de deploy já existe com esse mesmo nome. -settings.deploy_key_deletion=Remover chave de deploy +settings.deploy_key_deletion=Remover Chave de Deploy settings.deploy_key_deletion_desc=A exclusão de uma chave de deploy irá revogar o seu acesso a este repositório. Continuar? settings.deploy_key_deletion_success=A chave de deploy foi removida. settings.branches=Branches @@ -2304,15 +2304,15 @@ settings.protected_branch_can_push_yes=Você pode fazer push settings.protected_branch_can_push_no=Você não pode fazer push settings.protect_this_branch=Habilitar Proteção de Branch settings.protect_this_branch_desc=Previne a exclusão e restringe o merge e push para o branch. -settings.protect_disable_push=Desabilitar push +settings.protect_disable_push=Desabilitar Push settings.protect_disable_push_desc=Nenhum push será permitido neste branch. settings.protect_disable_force_push=Desativar Push Forçado settings.protect_disable_force_push_desc=Nenhum push forçado será permitido para este branch. settings.protect_enable_push=Habilitar Push settings.protect_enable_push_desc=Qualquer pessoa com acesso de escrita terá permissão para realizar push neste branch (mas não forçar o push). settings.protect_enable_force_push_all=Ativar Push Forçado -settings.protect_enable_merge=Permitir merge -settings.protect_check_status_contexts=Habilitar verificação de status +settings.protect_enable_merge=Permitir Merge +settings.protect_check_status_contexts=Habilitar Verificação de Status settings.protect_check_status_contexts_list=Verificações de status encontradas na última semana para este repositório settings.protect_required_approvals=Aprovações necessárias: settings.dismiss_stale_approvals=Descartar aprovações obsoletas @@ -2350,7 +2350,7 @@ settings.tags.protection.allowed=Permitido settings.tags.protection.allowed.users=Usuários permitidos settings.tags.protection.allowed.teams=Equipes permitidas settings.tags.protection.allowed.noone=Ninguém -settings.tags.protection.create=Proteger tag +settings.tags.protection.create=Proteger Tag settings.tags.protection.none=Não há tags protegidas. settings.bot_token=Token do Bot settings.chat_id=ID do Chat @@ -2359,7 +2359,7 @@ settings.matrix.room_id=ID da Sala settings.matrix.message_type=Tipo de Mensagem settings.visibility.private.button=Tornar Privado settings.visibility.public.button=Tornar Público -settings.archive.button=Arquivar repositório +settings.archive.button=Arquivar Repositório settings.archive.header=Arquivar Este Repositório settings.archive.success=O repositório foi arquivado com sucesso. settings.archive.error=Um erro ocorreu enquanto estava sendo arquivado o repositório. Veja o log para mais detalhes. @@ -2384,12 +2384,12 @@ settings.lfs_invalid_locking_path=Caminho inválido: %s settings.lfs_invalid_lock_directory=Não é possível bloquear o diretório: %s settings.lfs_lock_already_exists=O bloqueio já existe: %s settings.lfs_lock=Bloqueio -settings.lfs_locks_no_locks=Sem bloqueios +settings.lfs_locks_no_locks=Sem Bloqueios settings.lfs_lock_file_no_exist=Arquivo bloqueado não existe no branch padrão -settings.lfs_force_unlock=Forçar desbloqueio +settings.lfs_force_unlock=Forçar Desbloqueio settings.lfs_pointers.sha=SHA Blob settings.lfs_pointers.oid=OID -settings.lfs_pointers.inRepo=No repositório +settings.lfs_pointers.inRepo=No Repositório settings.lfs_pointers.exists=Existe na loja settings.lfs_pointers.accessible=Acessível ao Usuário settings.lfs_pointers.associateAccessible=Associar %d OIDs acessíveis @@ -2459,7 +2459,7 @@ releases.desc=Acompanhe as versões e downloads do projeto. release.releases=Versões release.detail=Detalhes da versão release.tags=Tags -release.new_release=Nova versão +release.new_release=Nova Versão release.draft=Rascunho release.prerelease=Versão prévia release.stable=Estável @@ -2479,15 +2479,15 @@ release.tag_helper_existing=Tag existente. release.title=Título da versão release.title_empty=O título não pode estar em branco. release.message=Descreva esta versão -release.prerelease_desc=Marcar como pré-lançamento +release.prerelease_desc=Marcar como Pré-lançamento release.prerelease_helper=Marcar esta versão como inadequada para uso em produção. release.cancel=Cancelar -release.publish=Publicar versão -release.save_draft=Salvar rascunho -release.edit_release=Atualizar versão +release.publish=Publicar Versão +release.save_draft=Salvar Rascunho +release.edit_release=Atualizar Versão release.delete_release=Excluir Versão release.delete_tag=Apagar Tag -release.deletion=Excluir versão +release.deletion=Excluir Versão release.deletion_desc=A exclusão de uma versão apenas a remove do Gitea. Isso não afetará a tag do Git, o conteúdo do seu repositório ou seu histórico. Continuar? release.deletion_success=A versão foi excluída. release.deletion_tag_desc=A tag será excluída do repositório. Conteúdo do repositório e histórico permanecerão inalterados. Continuar? @@ -3381,7 +3381,6 @@ swift.install=Adicione o pacote em seu arquivo Package.swift: swift.install2=e execute o seguinte comando: vagrant.install=Para adicionar uma Vagrant box, execute o seguinte comando: settings.link=Vincular este pacote a um repositório -settings.link.description=Se você vincular um pacote a um repositório, o pacote será listado na lista de pacotes do repositório. settings.link.select=Selecionar Repositório settings.link.button=Atualizar Link do Repositório settings.link.success=Link do repositório foi atualizado com sucesso. diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index c16677151c4..9e648b66023 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -3599,7 +3599,7 @@ no_subscriptions=Sem subscrições default_key=Assinado com a chave padrão error.extract_sign=Falhou ao extrair a assinatura error.generate_hash=Falhou ao gerar o hash do cometimento -error.no_committer_account=Não existe qualquer conta ligada ao endereço de email de quem cometeu +error.no_committer_account=Não existe qualquer conta vinculada ao endereço de email de quem cometeu error.no_gpg_keys_found=Não foi encontrada uma chave conhecida para esta assinatura, na base de dados error.not_signed_commit=Não é um cometimento assinado error.failed_retrieval_gpg_keys=Falhou ao obter uma chave ligada à conta de quem cometeu @@ -3617,7 +3617,7 @@ desc=Gerir pacotes do repositório. empty=Ainda não há pacotes. no_metadata=Sem metadados. empty.documentation=Para obter mais informação sobre o registo de pacotes, veja a documentação. -empty.repo=Carregou um pacote mas este não é apresentado aqui? Vá às configurações do pacote e ligue-o a este repositório. +empty.repo=Carregou um pacote mas este não é apresentado aqui? Vá às configurações do pacote e vincule-o a este repositório. registry.documentation=Para mais informação sobre o registo %s, veja a documentação. filter.type=Tipo filter.type.all=Todos @@ -3729,11 +3729,14 @@ swift.install=Adicione o pacote no seu ficheiro Package.swift: swift.install2=e execute o seguinte comando: vagrant.install=Para adicionar uma máquina virtual Vagrant, execute o seguinte comando: settings.link=Vincular este pacote a um repositório -settings.link.description=Se você vincular um pacote a um repositório, o pacote será listado na lista de pacotes do repositório. +settings.link.description=Se você vincular um pacote a um repositório, o pacote será listado na lista de pacotes do repositório. Apenas os repositórios do mesmo dono podem ser vinculados. Deixar o campo em branco irá remover o vínculo. settings.link.select=Escolha o repositório settings.link.button=Modificar vínculo ao repositório -settings.link.success=A ligação ao repositório foi modificada com sucesso. +settings.link.success=O vínculo ao repositório foi modificado com sucesso. settings.link.error=Falhou a modificação do vínculo ao repositório. +settings.link.repo_not_found=O repositório %s não foi encontrado. +settings.unlink.error=Falhou a remoção do vínculo ao repositório. +settings.unlink.success=O vínculo ao repositório foi removido com sucesso. settings.delete=Eliminar pacote settings.delete.description=Eliminar o pacote é permanente e não pode ser desfeito. settings.delete.notice=Está prestes a eliminar %s (%s). Esta operação é irreversível. Tem a certeza? diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 47aace96144..6da0eea91ce 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -3083,7 +3083,6 @@ swift.install=Добавьте пакет в свой файл Package.swi swift.install2=и запустите следующую команду: vagrant.install=Чтобы добавить бокс Vagrant, выполните следующую команду: settings.link=Связать этот пакет с репозиторием -settings.link.description=Если связать пакет с репозиторием, он добавится в список пакетов репозитория. settings.link.select=Выберите репозиторий settings.link.button=Обновить ссылку на репозиторий settings.link.success=Связь с репозиторием успешно обновлена. diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index 8b0e1aced26..a3ae736f67a 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -3418,7 +3418,6 @@ swift.install=Paketi Package.swift dosyanıza ekleyin: swift.install2=ve şu komutu çalıştırın: vagrant.install=Vagrant paketi eklemek için aşağıdaki komutu çalıştırın: settings.link=Bu paketi bir depoya bağlayın -settings.link.description=Eğer bir paketi bir depoya bağlarsanız, paket deponun paket listesinde listelenecektir. settings.link.select=Depo Seç settings.link.button=Depo Bağlantısını Güncelle settings.link.success=Depo bağlantısı başarıyla güncellendi. diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 85a9982d00a..1849d5d3782 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -3290,7 +3290,6 @@ swift.install=Додайте пакет у ваш файл Package.swiftPackage.swift 文件中添加该包: swift.install2=并运行以下命令: vagrant.install=若要添加一个 Vagrant box,请运行以下命令: settings.link=将此软件包链接到仓库 -settings.link.description=如果您将一个软件包与一个仓库链接起来,软件包将显示在仓库的软件包列表中。 settings.link.select=选择仓库 settings.link.button=更新仓库链接 settings.link.success=仓库链接已成功更新。 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index c82987ed73b..c1879203f7d 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -3397,7 +3397,6 @@ swift.install=將此套件加入您的 Package.swift 檔: swift.install2=並執行下列命令: vagrant.install=執行下列命令以新增 Vagrant box: settings.link=連結此套件到儲存庫 -settings.link.description=如果您將套件連結到儲存庫,該套件會顯示在儲存庫的套件清單。 settings.link.select=選擇儲存庫 settings.link.button=更新儲存庫連結 settings.link.success=儲存庫連結更新成功。 diff --git a/package.json b/package.json index 79aaa880a55..0b2ce033545 100644 --- a/package.json +++ b/package.json @@ -11,14 +11,15 @@ "@citation-js/plugin-csl": "0.7.18", "@citation-js/plugin-software-formats": "0.6.1", "@github/markdown-toolbar-element": "2.2.3", + "@github/paste-markdown": "1.5.3", "@github/relative-time-element": "4.4.8", "@github/text-expander-element": "2.9.2", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", - "@primer/octicons": "19.17.0", + "@primer/octicons": "19.18.0", "@resvg/resvg-wasm": "2.6.2", "@silverwind/vue3-calendar-heatmap": "2.0.6", "@techknowlogick/license-checker-webpack-plugin": "0.3.0", - "add-asset-webpack-plugin": "3.0.0", + "add-asset-webpack-plugin": "3.1.1", "ansi_up": "6.0.6", "asciinema-player": "3.10.0", "chart.js": "4.5.0", @@ -31,23 +32,21 @@ "dropzone": "6.0.0-beta.2", "easymde": "2.20.0", "esbuild-loader": "4.3.0", - "htmx.org": "2.0.6", + "htmx.org": "2.0.7", "idiomorph": "0.7.3", "jquery": "3.7.1", "katex": "0.16.22", "mermaid": "11.11.0", "mini-css-extract-plugin": "2.9.4", - "minimatch": "10.0.3", - "monaco-editor": "0.52.2", + "monaco-editor": "0.53.0", "monaco-editor-webpack-plugin": "7.1.0", "online-3d-viewer": "0.16.0", "pdfobject": "2.3.1", "perfect-debounce": "2.0.0", "postcss": "8.5.6", "postcss-loader": "8.2.0", - "postcss-nesting": "13.0.2", "sortablejs": "1.15.6", - "swagger-ui-dist": "5.28.1", + "swagger-ui-dist": "5.29.0", "tailwindcss": "3.4.17", "throttle-debounce": "5.0.2", "tinycolor2": "1.6.0", @@ -63,46 +62,44 @@ "vue-loader": "17.4.2", "webpack": "5.101.3", "webpack-cli": "6.0.1", - "wrap-ansi": "9.0.0" + "wrap-ansi": "9.0.2" }, "devDependencies": { "@eslint-community/eslint-plugin-eslint-comments": "4.5.0", "@playwright/test": "1.55.0", "@stoplight/spectral-cli": "6.15.0", - "@stylistic/eslint-plugin-js": "3.1.0", + "@stylistic/eslint-plugin": "5.3.1", "@stylistic/stylelint-plugin": "4.0.0", "@types/codemirror": "5.60.16", "@types/dropzone": "5.7.9", "@types/jquery": "3.5.33", "@types/katex": "0.16.7", - "@types/license-checker-webpack-plugin": "0.2.5", "@types/pdfobject": "2.2.5", "@types/sortablejs": "1.15.8", "@types/swagger-ui-dist": "3.30.6", "@types/throttle-debounce": "5.0.2", "@types/tinycolor2": "1.4.6", "@types/toastify-js": "1.12.4", - "@typescript-eslint/eslint-plugin": "8.42.0", - "@typescript-eslint/parser": "8.42.0", + "@typescript-eslint/parser": "8.43.0", "@vitejs/plugin-vue": "6.0.1", "@vitest/eslint-plugin": "1.3.9", - "eslint": "8.57.0", + "eslint": "9.35.0", "eslint-import-resolver-typescript": "4.4.4", - "eslint-plugin-array-func": "4.0.0", - "eslint-plugin-github": "5.0.2", + "eslint-plugin-array-func": "5.0.2", + "eslint-plugin-github": "6.0.0", "eslint-plugin-import-x": "4.16.1", - "eslint-plugin-no-jquery": "3.1.1", - "eslint-plugin-no-use-extend-native": "0.5.0", + "eslint-plugin-no-use-extend-native": "0.7.2", "eslint-plugin-playwright": "2.2.2", "eslint-plugin-regexp": "2.10.0", "eslint-plugin-sonarjs": "3.0.5", - "eslint-plugin-unicorn": "56.0.1", + "eslint-plugin-unicorn": "61.0.2", "eslint-plugin-vue": "10.4.0", "eslint-plugin-vue-scoped-css": "2.12.0", "eslint-plugin-wc": "3.0.1", + "globals": "16.4.0", "happy-dom": "18.0.1", "markdownlint-cli": "0.45.0", - "material-icon-theme": "5.26.0", + "material-icon-theme": "5.27.0", "nolyfill": "1.0.44", "postcss-html": "1.8.0", "stylelint": "16.24.0", @@ -111,8 +108,8 @@ "stylelint-declaration-strict-value": "1.10.11", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "4.0.0", - "type-fest": "4.41.0", - "updates": "16.6.4", + "typescript-eslint": "8.43.0", + "updates": "16.7.0", "vite-string-plugin": "1.4.6", "vitest": "3.2.4", "vue-tsc": "3.0.6" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 149d8f179c1..5d40acd1019 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: '@github/markdown-toolbar-element': specifier: 2.2.3 version: 2.2.3 + '@github/paste-markdown': + specifier: 1.5.3 + version: 1.5.3 '@github/relative-time-element': specifier: 4.4.8 version: 4.4.8 @@ -51,8 +54,8 @@ importers: specifier: 0.1.0-alpha-3 version: 0.1.0-alpha-3 '@primer/octicons': - specifier: 19.17.0 - version: 19.17.0 + specifier: 19.18.0 + version: 19.18.0 '@resvg/resvg-wasm': specifier: 2.6.2 version: 2.6.2 @@ -63,8 +66,8 @@ importers: specifier: 0.3.0 version: 0.3.0(webpack@5.101.3) add-asset-webpack-plugin: - specifier: 3.0.0 - version: 3.0.0(webpack@5.101.3) + specifier: 3.1.1 + version: 3.1.1(webpack@5.101.3) ansi_up: specifier: 6.0.6 version: 6.0.6 @@ -102,8 +105,8 @@ importers: specifier: 4.3.0 version: 4.3.0(webpack@5.101.3) htmx.org: - specifier: 2.0.6 - version: 2.0.6 + specifier: 2.0.7 + version: 2.0.7 idiomorph: specifier: 0.7.3 version: 0.7.3 @@ -119,15 +122,12 @@ importers: mini-css-extract-plugin: specifier: 2.9.4 version: 2.9.4(webpack@5.101.3) - minimatch: - specifier: 10.0.3 - version: 10.0.3 monaco-editor: - specifier: 0.52.2 - version: 0.52.2 + specifier: 0.53.0 + version: 0.53.0 monaco-editor-webpack-plugin: specifier: 7.1.0 - version: 7.1.0(monaco-editor@0.52.2)(webpack@5.101.3) + version: 7.1.0(monaco-editor@0.53.0)(webpack@5.101.3) online-3d-viewer: specifier: 0.16.0 version: 0.16.0 @@ -143,15 +143,12 @@ importers: postcss-loader: specifier: 8.2.0 version: 8.2.0(postcss@8.5.6)(typescript@5.9.2)(webpack@5.101.3) - postcss-nesting: - specifier: 13.0.2 - version: 13.0.2(postcss@8.5.6) sortablejs: specifier: 1.15.6 version: 1.15.6 swagger-ui-dist: - specifier: 5.28.1 - version: 5.28.1 + specifier: 5.29.0 + version: 5.29.0 tailwindcss: specifier: 3.4.17 version: 3.4.17 @@ -198,21 +195,21 @@ importers: specifier: 6.0.1 version: 6.0.1(webpack@5.101.3) wrap-ansi: - specifier: 9.0.0 - version: 9.0.0 + specifier: 9.0.2 + version: 9.0.2 devDependencies: '@eslint-community/eslint-plugin-eslint-comments': specifier: 4.5.0 - version: 4.5.0(eslint@8.57.0) + version: 4.5.0(eslint@9.35.0(jiti@2.5.1)) '@playwright/test': specifier: 1.55.0 version: 1.55.0 '@stoplight/spectral-cli': specifier: 6.15.0 version: 6.15.0 - '@stylistic/eslint-plugin-js': - specifier: 3.1.0 - version: 3.1.0(eslint@8.57.0) + '@stylistic/eslint-plugin': + specifier: 5.3.1 + version: 5.3.1(eslint@9.35.0(jiti@2.5.1)) '@stylistic/stylelint-plugin': specifier: 4.0.0 version: 4.0.0(stylelint@16.24.0(typescript@5.9.2)) @@ -228,9 +225,6 @@ importers: '@types/katex': specifier: 0.16.7 version: 0.16.7 - '@types/license-checker-webpack-plugin': - specifier: 0.2.5 - version: 0.2.5 '@types/pdfobject': specifier: 2.2.5 version: 2.2.5 @@ -249,60 +243,57 @@ importers: '@types/toastify-js': specifier: 1.12.4 version: 1.12.4 - '@typescript-eslint/eslint-plugin': - specifier: 8.42.0 - version: 8.42.0(@typescript-eslint/parser@8.42.0(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0)(typescript@5.9.2) '@typescript-eslint/parser': - specifier: 8.42.0 - version: 8.42.0(eslint@8.57.0)(typescript@5.9.2) + specifier: 8.43.0 + version: 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) '@vitejs/plugin-vue': specifier: 6.0.1 - version: 6.0.1(vite@7.1.4(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2)) + version: 6.0.1(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2)) '@vitest/eslint-plugin': specifier: 1.3.9 - version: 1.3.9(eslint@8.57.0)(typescript@5.9.2)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.1)(happy-dom@18.0.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1)) + version: 1.3.9(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.1)(happy-dom@18.0.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1)) eslint: - specifier: 8.57.0 - version: 8.57.0 + specifier: 9.35.0 + version: 9.35.0(jiti@2.5.1) eslint-import-resolver-typescript: specifier: 4.4.4 - version: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.42.0(eslint@8.57.0)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0))(eslint-plugin-import@2.32.0)(eslint@8.57.0) + version: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.35.0(jiti@2.5.1)))(eslint-plugin-import@2.32.0)(eslint@9.35.0(jiti@2.5.1)) eslint-plugin-array-func: - specifier: 4.0.0 - version: 4.0.0(eslint@8.57.0) - eslint-plugin-github: specifier: 5.0.2 - version: 5.0.2(@types/eslint@9.6.1)(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.0)(typescript@5.9.2) + version: 5.0.2(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-github: + specifier: 6.0.0 + version: 6.0.0(@types/eslint@9.6.1)(eslint-import-resolver-typescript@4.4.4)(eslint@9.35.0(jiti@2.5.1)) eslint-plugin-import-x: specifier: 4.16.1 - version: 4.16.1(@typescript-eslint/utils@8.42.0(eslint@8.57.0)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) - eslint-plugin-no-jquery: - specifier: 3.1.1 - version: 3.1.1(eslint@8.57.0) + version: 4.16.1(@typescript-eslint/utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.35.0(jiti@2.5.1)) eslint-plugin-no-use-extend-native: - specifier: 0.5.0 - version: 0.5.0 + specifier: 0.7.2 + version: 0.7.2(eslint@9.35.0(jiti@2.5.1)) eslint-plugin-playwright: specifier: 2.2.2 - version: 2.2.2(eslint@8.57.0) + version: 2.2.2(eslint@9.35.0(jiti@2.5.1)) eslint-plugin-regexp: specifier: 2.10.0 - version: 2.10.0(eslint@8.57.0) + version: 2.10.0(eslint@9.35.0(jiti@2.5.1)) eslint-plugin-sonarjs: specifier: 3.0.5 - version: 3.0.5(eslint@8.57.0) + version: 3.0.5(eslint@9.35.0(jiti@2.5.1)) eslint-plugin-unicorn: - specifier: 56.0.1 - version: 56.0.1(eslint@8.57.0) + specifier: 61.0.2 + version: 61.0.2(eslint@9.35.0(jiti@2.5.1)) eslint-plugin-vue: specifier: 10.4.0 - version: 10.4.0(@typescript-eslint/parser@8.42.0(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0)(vue-eslint-parser@10.2.0(eslint@8.57.0)) + version: 10.4.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1))(vue-eslint-parser@10.2.0(eslint@9.35.0(jiti@2.5.1))) eslint-plugin-vue-scoped-css: specifier: 2.12.0 - version: 2.12.0(eslint@8.57.0)(vue-eslint-parser@10.2.0(eslint@8.57.0)) + version: 2.12.0(eslint@9.35.0(jiti@2.5.1))(vue-eslint-parser@10.2.0(eslint@9.35.0(jiti@2.5.1))) eslint-plugin-wc: specifier: 3.0.1 - version: 3.0.1(eslint@8.57.0) + version: 3.0.1(eslint@9.35.0(jiti@2.5.1)) + globals: + specifier: 16.4.0 + version: 16.4.0 happy-dom: specifier: 18.0.1 version: 18.0.1 @@ -310,8 +301,8 @@ importers: specifier: 0.45.0 version: 0.45.0 material-icon-theme: - specifier: 5.26.0 - version: 5.26.0 + specifier: 5.27.0 + version: 5.27.0 nolyfill: specifier: 1.0.44 version: 1.0.44 @@ -336,12 +327,12 @@ importers: svgo: specifier: 4.0.0 version: 4.0.0 - type-fest: - specifier: 4.41.0 - version: 4.41.0 + typescript-eslint: + specifier: 8.43.0 + version: 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) updates: - specifier: 16.6.4 - version: 16.6.4 + specifier: 16.7.0 + version: 16.7.0 vite-string-plugin: specifier: 1.4.6 version: 1.4.6 @@ -475,12 +466,6 @@ packages: '@csstools/css-parser-algorithms': ^3.0.5 '@csstools/css-tokenizer': ^3.0.4 - '@csstools/selector-resolve-nested@3.1.0': - resolution: {integrity: sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==} - engines: {node: '>=18'} - peerDependencies: - postcss-selector-parser: ^7.0.0 - '@csstools/selector-specificity@5.0.0': resolution: {integrity: sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==} engines: {node: '>=18'} @@ -665,8 +650,8 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 - '@eslint-community/eslint-utils@4.8.0': - resolution: {integrity: sha512-MJQFqrZgcW0UNYLGOuQpey/oTN59vyWwplvCGZztn1cKz9agZPPYpJB7h2OMmuu7VLqkvEjN8feFZJmxNF9D+Q==} + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -675,13 +660,42 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/compat@1.3.2': + resolution: {integrity: sha512-jRNwzTbd6p2Rw4sZ1CgWRS8YMtqG15YyZf7zvb6gY2rB2u6n+2Z+ELW0GtL0fQgyl0pr4Y/BzBfng/BdsereRA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.40 || 9 + peerDependenciesMeta: + eslint: + optional: true - '@eslint/js@8.57.0': - resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/config-array@0.21.0': + resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.3.1': + resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.15.2': + resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.35.0': + resolution: {integrity: sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.3.5': + resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@github/browserslist-config@1.0.0': resolution: {integrity: sha512-gIhjdJp/c2beaIWWIlsXdqXVRUz3r2BxBCpfz/F3JXHvSAQ1paMYjLH+maEATtENg+k5eLV7gA+9yPp762ieuw==} @@ -692,24 +706,30 @@ packages: '@github/markdown-toolbar-element@2.2.3': resolution: {integrity: sha512-AlquKGee+IWiAMYVB0xyHFZRMnu4n3X4HTvJHu79GiVJ1ojTukCWyxMlF5NMsecoLcBKsuBhx3QPv2vkE/zQ0A==} + '@github/paste-markdown@1.5.3': + resolution: {integrity: sha512-PzZ1b3PaqBzYqbT4fwKEhiORf38h2OcGp2+JdXNNM7inZ7egaSmfmhyNkQILpqWfS0AYtRS3CDq6z03eZ8yOMQ==} + '@github/relative-time-element@4.4.8': resolution: {integrity: sha512-FSLYm6F3TSQnqHE1EMQUVVgi2XjbCvsESwwXfugHFpBnhyF1uhJOtu0Psp/BB/qqazfdkk7f5fVcu7WuXl3t8Q==} '@github/text-expander-element@2.9.2': resolution: {integrity: sha512-XY8EUMqM4GAloNxXNA1Py1ny+engWwYntbgsnpstQN4piaTI9rIlfYldyd0nnPXhxjGCVqHPmP6yg17Q0/n9Vg==} - '@humanwhocodes/config-array@0.11.14': - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/object-schema@2.0.3': - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -876,8 +896,8 @@ packages: '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - '@primer/octicons@19.17.0': - resolution: {integrity: sha512-uM3ET0WC2aZMetxv7CRfIw3FexIo2fYtw2EgxqLFzLjn/rh+dIQWu781chLHh9d2mSNlxedauHLQmbYi0Dd/Mg==} + '@primer/octicons@19.18.0': + resolution: {integrity: sha512-4P8FS7slUp0vHE7zLtlfOQcgF99r0tdxpriT3ahp6iwwnf1hF3OlDWXEp5n6b6G5eIY1cNr1bCrjRL+DkRjFVw==} '@resvg/resvg-wasm@2.6.2': resolution: {integrity: sha512-FqALmHI8D4o6lk/LRWDnhw95z5eO+eAa6ORjVg09YRR7BkcM6oPHU9uyC0gtQG5vpFLvgpeU4+zEAz2H8APHNw==} @@ -1116,11 +1136,11 @@ packages: resolution: {integrity: sha512-JZlVFE6/dYpP9tQmV0/ADfn32L9uFarHWxfcRhReKUnljz1ZiUM5zpX+PH8h5CJs6lao3TuFqnPm9IJJCEkE2w==} engines: {node: '>=10.8'} - '@stylistic/eslint-plugin-js@3.1.0': - resolution: {integrity: sha512-lQktsOiCr8S6StG29C5fzXYxLOD6ID1rp4j6TRS+E/qY1xd59Fm7dy5qm9UauJIEoSTlYx6yGsCHYh5UkgXPyg==} + '@stylistic/eslint-plugin@5.3.1': + resolution: {integrity: sha512-Ykums1VYonM0TgkD0VteVq9mrlO2FhF48MDJnPyv3MktIB2ydtuhlO0AfWm7xnW1kyf5bjOqA6xc7JjviuVTxg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: '>=8.40.0' + eslint: '>=9.0.0' '@stylistic/stylelint-plugin@4.0.0': resolution: {integrity: sha512-CFwt3K4Y/7bygNCLCQ8Sy4Hzgbhxq3BsNW0FIuYxl17HD3ywptm54ocyeiLVRrk5jtz1Zwks7Xr9eiZt8SWHAw==} @@ -1280,9 +1300,6 @@ packages: '@types/katex@0.16.7': resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} - '@types/license-checker-webpack-plugin@0.2.5': - resolution: {integrity: sha512-raj3YPZxjkDlRrJJUq6So+C9i/fqYxUtVZzlXxm6bwwXONOnTHvGCfYmbQhzsHJneiJdlR6mMH76/bvWbZZN8Q==} - '@types/markdown-escape@1.1.3': resolution: {integrity: sha512-JIc1+s3y5ujKnt/+N+wq6s/QdL2qZ11fP79MijrVXsAAnzSxCbT2j/3prHRouJdZ2yFLN3vkP0HytfnoCczjOw==} @@ -1298,9 +1315,6 @@ packages: '@types/node@24.3.1': resolution: {integrity: sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==} - '@types/normalize-package-data@2.4.4': - resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} - '@types/pdfobject@2.2.5': resolution: {integrity: sha512-7gD5tqc/RUDq0PyoLemL0vEHxBYi+zY0WVaFAx/Y0jBsXFgot1vB9No1GhDZGwRGJMCIZbgAb74QG9MTyTNU/g==} @@ -1313,15 +1327,9 @@ packages: '@types/sortablejs@1.15.8': resolution: {integrity: sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==} - '@types/source-list-map@0.1.6': - resolution: {integrity: sha512-5JcVt1u5HDmlXkwOD2nslZVllBBc7HDuOICfiZah2Z0is8M8g+ddAEawbmd3VjedfDHBzxCaXLs07QEmb7y54g==} - '@types/swagger-ui-dist@3.30.6': resolution: {integrity: sha512-FVxN7wjLYRtJsZBscOcOcf8oR++m38vbUFjT33Mr9HBuasX9bRDrJsp7iwixcOtKSHEEa2B7o2+4wEiXqC+Ebw==} - '@types/tapable@1.0.12': - resolution: {integrity: sha512-bTHG8fcxEqv1M9+TD14P8ok8hjxoOCkfKc8XXLaaD05kI7ohpeI956jtDOD3XHKBQrlyPughUtzm1jtVhHpA5Q==} - '@types/tern@0.23.9': resolution: {integrity: sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==} @@ -1334,89 +1342,80 @@ packages: '@types/toastify-js@1.12.4': resolution: {integrity: sha512-zfZHU4tKffPCnZRe7pjv/eFKzTVHozKewFCKaCjZ4gFinKgJRz/t0bkZiMCXJxPhv/ZoeDGNOeRD09R0kQZ/nw==} + '@types/trusted-types@1.0.6': + resolution: {integrity: sha512-230RC8sFeHoT6sSUlRO6a8cAnclO06eeiq1QDfiv2FGCLWFvvERWgwIQD4FWqD9A69BN7Lzee4OXwoMVnnsWDw==} + '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - '@types/uglify-js@3.17.5': - resolution: {integrity: sha512-TU+fZFBTBcXj/GpDpDaBmgWk/gn96kMZ+uocaFUlV2f8a6WdMzzI44QBCmGcCiYR0Y6ZlNRiyUyKKt5nl/lbzQ==} - '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} '@types/urijs@1.19.25': resolution: {integrity: sha512-XOfUup9r3Y06nFAZh3WvO0rBU4OtlfPB/vgxpjg+NRdGU6CN6djdc6OEiH+PcqHCY6eFLo9Ista73uarf4gnBg==} - '@types/webpack-sources@3.2.3': - resolution: {integrity: sha512-4nZOdMwSPHZ4pTEZzSp0AsTM4K7Qmu40UKW4tJDiOVs20UzYF9l+qUe4s0ftfN0pin06n+5cWWDJXH+sbhAiDw==} - - '@types/webpack@4.41.40': - resolution: {integrity: sha512-u6kMFSBM9HcoTpUXnL6mt2HSzftqb3JgYV6oxIgL2dl6sX6aCa5k6SOkzv5DuZjBTPUE/dJltKtwwuqrkZHpfw==} - '@types/whatwg-mimetype@3.0.2': resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} - '@typescript-eslint/eslint-plugin@8.42.0': - resolution: {integrity: sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ==} + '@typescript-eslint/eslint-plugin@8.43.0': + resolution: {integrity: sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.42.0 + '@typescript-eslint/parser': ^8.43.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.42.0': - resolution: {integrity: sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==} + '@typescript-eslint/parser@8.43.0': + resolution: {integrity: sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.42.0': - resolution: {integrity: sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==} + '@typescript-eslint/project-service@8.43.0': + resolution: {integrity: sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.42.0': - resolution: {integrity: sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==} + '@typescript-eslint/scope-manager@8.43.0': + resolution: {integrity: sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.42.0': - resolution: {integrity: sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==} + '@typescript-eslint/tsconfig-utils@8.43.0': + resolution: {integrity: sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.42.0': - resolution: {integrity: sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ==} + '@typescript-eslint/type-utils@8.43.0': + resolution: {integrity: sha512-qaH1uLBpBuBBuRf8c1mLJ6swOfzCXryhKND04Igr4pckzSEW9JX5Aw9AgW00kwfjWJF0kk0ps9ExKTfvXfw4Qg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.42.0': - resolution: {integrity: sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==} + '@typescript-eslint/types@8.43.0': + resolution: {integrity: sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.42.0': - resolution: {integrity: sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==} + '@typescript-eslint/typescript-estree@8.43.0': + resolution: {integrity: sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.42.0': - resolution: {integrity: sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g==} + '@typescript-eslint/utils@8.43.0': + resolution: {integrity: sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.42.0': - resolution: {integrity: sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==} + '@typescript-eslint/visitor-keys@8.43.0': + resolution: {integrity: sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@ungap/structured-clone@1.3.0': - resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - '@unrs/resolver-binding-android-arm-eabi@1.11.1': resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} cpu: [arm] @@ -1705,11 +1704,14 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - add-asset-webpack-plugin@3.0.0: - resolution: {integrity: sha512-mg6nL4E+dNZPQfTc/A4xcOYsIxCN7Cuy9OScZsjI9qOYPiJeFZfsYcdoYlqcNNQEC4w8yNwArhD1Gm+G1iWrNg==} + add-asset-webpack-plugin@3.1.1: + resolution: {integrity: sha512-0WexE8uFq2hkC/rc+zVY8Hf5cKj/UwuBa0GSDKfCiKh6rrw/e7PYDgdxz1syHXIthTRlgt5q2vLvBIWcWtAvIQ==} engines: {node: '>=18'} peerDependencies: webpack: '>=5' + peerDependenciesMeta: + webpack: + optional: true ajv-draft-04@1.0.0: resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} @@ -1750,16 +1752,16 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.2.0: - resolution: {integrity: sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} ansi_up@6.0.6: @@ -1872,6 +1874,10 @@ packages: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} + builtin-modules@5.0.0: + resolution: {integrity: sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==} + engines: {node: '>=18.20'} + builtins@1.0.3: resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} @@ -1905,6 +1911,9 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + change-case@5.4.4: + resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + character-entities-legacy@3.0.0: resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} @@ -2349,10 +2358,6 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - dom-input-range@2.0.1: resolution: {integrity: sha512-UMGnuQlEepIPCju5NrPe+8y52B+pRvSjIaSPW92KpukoDGSEVkI2iaCR4HxK7K6C7zmVsWE8PEjCYZaJMr3vpg==} @@ -2381,8 +2386,8 @@ packages: easymde@2.20.0: resolution: {integrity: sha512-V1Z5f92TfR42Na852OWnIZMbM7zotWQYTddNaLYZFVKj7APBbyZ3FYJ27gBw2grMW3R6Qdv9J8n5Ij7XRSIgXQ==} - electron-to-chromium@1.5.214: - resolution: {integrity: sha512-TpvUNdha+X3ybfU78NoQatKvQEm1oq3lf2QbnmCEdw+Bd9RuIAY+hJTvq1avzHM0f7EJfnH3vbCnbzKzisc/9Q==} + electron-to-chromium@1.5.215: + resolution: {integrity: sha512-TIvGp57UpeNetj/wV/xpFNpWGb0b/ROw372lHPx5Aafx02gjTBtWnEEcaSX3W2dLM3OSdGGyHX/cHl01JQsLaQ==} emoji-regex@10.5.0: resolution: {integrity: sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==} @@ -2500,11 +2505,11 @@ packages: eslint-import-resolver-webpack: optional: true - eslint-plugin-array-func@4.0.0: - resolution: {integrity: sha512-p3NY2idNIvgmQLF2/62ZskYt8gOuUgQ51smRc3Lh7FtSozpNc2sg+lniz9VaCagLZHEZTl8qGJKqE7xy8O/D/g==} + eslint-plugin-array-func@5.0.2: + resolution: {integrity: sha512-iyLex2+pTcxHZ6OLL80oMy+CtffpJ9j6A/57VQi1VN5bK1IS/0o+mWvezDHeAlwXjn6ksRO9L5SGU329BBuY8A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - eslint: '>=8.40.0' + eslint: '>=8.51.0' eslint-plugin-escompat@3.11.4: resolution: {integrity: sha512-j0ywwNnIufshOzgAu+PfIig1c7VRClKSNKzpniMT2vXQ4leL5q+e/SpMFQU0nrdL2WFFM44XmhSuwmxb3G0CJg==} @@ -2522,11 +2527,11 @@ packages: peerDependencies: eslint: '*' - eslint-plugin-github@5.0.2: - resolution: {integrity: sha512-nMdzWJQ5CimjQDY6SFeJ0KIXuNFf0dgDWEd4eP3UWfuTuP/dXcZJDg7MQRvAFt743T1zUi4+/HdOihfu8xJkLA==} + eslint-plugin-github@6.0.0: + resolution: {integrity: sha512-J8MvUoiR/TU/Y9NnEmg1AnbvMUj9R6IO260z47zymMLLvso7B4c80IKjd8diqmqtSmeXXlbIus4i0SvK84flag==} hasBin: true peerDependencies: - eslint: ^8.0.1 + eslint: ^8 || ^9 eslint-plugin-i18n-text@1.0.1: resolution: {integrity: sha512-3G3UetST6rdqhqW9SfcfzNYMpQXS7wNkJvp6dsXnjzGiku6Iu5hl3B0kmk6lIcFPwYjhQIY+tXVRtK9TlGT7RA==} @@ -2562,18 +2567,15 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 - eslint-plugin-no-jquery@3.1.1: - resolution: {integrity: sha512-LTLO3jH/Tjr1pmxCEqtV6qmt+OChv8La4fwgG470JRpgxyFF4NOzoC9CRy92GIWD3Yjl0qLEgPmD2FLQWcNEjg==} - peerDependencies: - eslint: '>=8.0.0' - eslint-plugin-no-only-tests@3.3.0: resolution: {integrity: sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==} engines: {node: '>=5.0.0'} - eslint-plugin-no-use-extend-native@0.5.0: - resolution: {integrity: sha512-dBNjs8hor8rJgeXLH4HTut5eD3RGWf9JUsadIfuL7UosVQ/dnvOKwxEcRrXrFxrMZ8llUVWT+hOimxJABsAUzQ==} - engines: {node: '>=6.0.0'} + eslint-plugin-no-use-extend-native@0.7.2: + resolution: {integrity: sha512-hUBlwaTXIO1GzTwPT6pAjvYwmSHe4XduDhAiQvur4RUujmBUFjd8Nb2+e7WQdsQ+nGHWGRlogcUWXJRGqizTWw==} + engines: {node: '>=18.18.0'} + peerDependencies: + eslint: ^9.3.0 eslint-plugin-playwright@2.2.2: resolution: {integrity: sha512-j0jKpndIPOXRRP9uMkwb9l/nSmModOU3452nrFdgFJoEv/435J1onk8+aITzjDW8DfypxgmVaDMdmVIa6F7I0w==} @@ -2606,11 +2608,11 @@ packages: peerDependencies: eslint: ^8.0.0 || ^9.0.0 - eslint-plugin-unicorn@56.0.1: - resolution: {integrity: sha512-FwVV0Uwf8XPfVnKSGpMg7NtlZh0G0gBarCaFcMUOoqPxXryxdYxTRRv4kH6B9TFCVIrjRXG+emcxIk2ayZilog==} - engines: {node: '>=18.18'} + eslint-plugin-unicorn@61.0.2: + resolution: {integrity: sha512-zLihukvneYT7f74GNbVJXfWIiNQmkc/a9vYBTE4qPkQZswolWNdu+Wsp9sIXno1JOzdn6OUwLPd19ekXVkahRA==} + engines: {node: ^20.10.0 || >=21.0.0} peerDependencies: - eslint: '>=8.56.0' + eslint: '>=9.29.0' eslint-plugin-vue-scoped-css@2.12.0: resolution: {integrity: sha512-gEbuvYetNbsPA0IsmERFkVC2/vOHCInfFekNSOsAxWI/7C/bc8PoLal+fRibWfnzWryY6iL8YoluMtrEqWRj1A==} @@ -2643,10 +2645,6 @@ packages: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-scope@8.4.0: resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2659,20 +2657,20 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + eslint@9.35.0: + resolution: {integrity: sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true espree@10.4.0: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - esquery@1.6.0: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} @@ -2771,14 +2769,18 @@ packages: file-entry-cache@10.1.4: resolution: {integrity: sha512-5XRUFc0WTtUbjfGzEwXc42tiGxQHBmtbUG1h9L2apu4SulCGN3Hqm//9D6FAolf8MYNL7f/YlJl9vy08pj5JuA==} - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + find-up-simple@1.0.1: + resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} + engines: {node: '>=18'} + find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -2787,9 +2789,9 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} flat-cache@6.1.13: resolution: {integrity: sha512-gmtS2PaUjSPa4zjObEIn4WWliKyZzYljgxODBfxugpK6q6HU9ClXzgCJ+nlcPKY9Bt090ypTOLIFWkV0jbKFjw==} @@ -2829,13 +2831,13 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-east-asian-width@1.3.1: - resolution: {integrity: sha512-R1QfovbPsKmosqTnPoRFiJ7CF9MLRgb53ChvMZm+r4p76/+8yKDy17qLL2PKInORy2RkZZekuK0efYgmzTkXyQ==} + get-east-asian-width@1.4.0: + resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} engines: {node: '>=18'} - get-set-props@0.1.0: - resolution: {integrity: sha512-7oKuKzAGKj0ag+eWZwcGw2fjiZ78tXnXQoBgY0aU7ZOxTu4bB7hSuQSDgtKy978EDH062P5FmD2EWiDpQS9K9Q==} - engines: {node: '>=0.10.0'} + get-set-props@0.2.0: + resolution: {integrity: sha512-YCmOj+4YAeEB5Dd9jfp6ETdejMet4zSxXjNkgaa4npBEKRI9uDOGB5MmAdAgi2OoFGAKshYhCbmLq2DS03CgVA==} + engines: {node: '>=18.0.0'} get-source@2.0.12: resolution: {integrity: sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==} @@ -2879,10 +2881,18 @@ packages: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + globals@15.15.0: resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} engines: {node: '>=18'} + globals@16.4.0: + resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} + engines: {node: '>=18'} + globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -2921,9 +2931,6 @@ packages: hookified@1.12.0: resolution: {integrity: sha512-hMr1Y9TCLshScrBbV2QxJ9BROddxZ12MX9KsCtuGGy/3SmmN5H1PllKerrVlSotur9dlE8hmUKAOSa3WDzsZmQ==} - hosted-git-info@2.8.9: - resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - hpagent@1.2.0: resolution: {integrity: sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==} engines: {node: '>=14'} @@ -2935,8 +2942,8 @@ packages: htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} - htmx.org@2.0.6: - resolution: {integrity: sha512-7ythjYneGSk3yCHgtCnQeaoF+D+o7U2LF37WU3O0JYv3gTZSicdEFiI/Ai/NJyC5ZpYJWMpUb11OC5Lr6AfAqA==} + htmx.org@2.0.7: + resolution: {integrity: sha512-YiJqF3U5KyO28VC5mPfehKJPF+n1Gni+cupK+D69TF0nm7wY6AXn3a4mPWIikfAXtl1u1F1+ZhSCS7KT8pVmqA==} iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} @@ -2978,9 +2985,9 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} @@ -3023,9 +3030,9 @@ packages: is-buffer@1.1.6: resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} - is-builtin-module@3.2.1: - resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} - engines: {node: '>=6'} + is-builtin-module@5.0.0: + resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==} + engines: {node: '>=18.20'} is-bun-module@2.0.0: resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} @@ -3041,8 +3048,9 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-get-set-prop@1.0.0: - resolution: {integrity: sha512-DvAYZ1ZgGUz4lzxKMPYlt08qAUqyG9ckSg2pIjfvcQ7+pkVNUHk8yVLXOnCLe5WKXhLop8oorWFBJHpwWQpszQ==} + is-get-set-prop@2.0.0: + resolution: {integrity: sha512-C32bqXfHJfRwa0U5UIMqSGziZhALszXDJZ8n8mz8WZ6c6V7oYGHEWwJvftliBswypY3P3EQqdY5lpDSEKvTS1Q==} + engines: {node: '> 18.0.0'} is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} @@ -3051,19 +3059,17 @@ packages: is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} - is-js-type@2.0.0: - resolution: {integrity: sha512-Aj13l47+uyTjlQNHtXBV8Cji3jb037vxwMWCgopRR8h6xocgBGW3qG8qGlIOEmbXQtkKShKuBM9e8AA1OeQ+xw==} + is-js-type@3.0.0: + resolution: {integrity: sha512-IbPf3g3vxm1D902xaBaYp2TUHiXZWwWRu5bM9hgKN9oAQcFaKALV6Gd13PGhXjKE5u2n8s1PhLhdke/E1fchxQ==} + engines: {node: '>=18.0.0'} is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-obj-prop@1.0.0: - resolution: {integrity: sha512-5Idb61slRlJlsAzi0Wsfwbp+zZY+9LXKUAZpvT/1ySw+NxKLRWfa0Bzj+wXI3fX5O9hiddm5c3DAaRSNP/yl2w==} - - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} + is-obj-prop@2.0.0: + resolution: {integrity: sha512-2/VFrbzXSZVJIscazpxoB+pOQx2jBOAAL9Gui4cRKxflznUNBpsr8IDvBA4UGol3e40sltLNiY3qnZv/7qSUxA==} + engines: {node: '>=18.0.0'} is-plain-object@2.0.4: resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} @@ -3076,8 +3082,9 @@ packages: is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - is-proto-prop@2.0.0: - resolution: {integrity: sha512-jl3NbQ/fGLv5Jhan4uX+Ge9ohnemqyblWVVCpAvtTQzNFvV2xhJq+esnkIbYQ9F1nITXoLfDDQLp7LBw/zzncg==} + is-proto-prop@3.0.1: + resolution: {integrity: sha512-S8xSxNMGJO4eZD86kO46zrq2gLIhA+rN9443lQEvt8Mz/l8cxk72p/AWFmofY6uL9g9ILD6cXW6j8QQj4F3Hcw==} + engines: {node: '>=18.0.0'} is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} @@ -3123,9 +3130,9 @@ packages: js-tokens@9.0.1: resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} - js-types@1.0.0: - resolution: {integrity: sha512-bfwqBW9cC/Lp7xcRpug7YrXm0IVw+T9e3g4mCYnv0Pjr3zIzU9PCQElYU9oSGAWzXlbdl9X5SAMPejO9sxkeUw==} - engines: {node: '>=0.10.0'} + js-types@4.0.0: + resolution: {integrity: sha512-/c+n06zvqFQGxdz1BbElF7S3nEghjNchLN1TjQnk2j10HYDaUc57rcvl6BbnziTx8NQmrg0JOs/iwRpvcYaxjQ==} + engines: {node: '>=18.20'} js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} @@ -3139,8 +3146,9 @@ packages: resolution: {integrity: sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==} engines: {node: '>= 10.16.0'} - jsesc@0.5.0: - resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} + jsesc@3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} hasBin: true jsesc@3.1.0: @@ -3316,9 +3324,9 @@ packages: loupe@3.2.1: resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} - lowercase-keys@1.0.1: - resolution: {integrity: sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==} - engines: {node: '>=0.10.0'} + lowercase-keys@3.0.0: + resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -3330,8 +3338,8 @@ packages: magic-string@0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} - magic-string@0.30.18: - resolution: {integrity: sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==} + magic-string@0.30.19: + resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} markdown-escape@2.0.0: resolution: {integrity: sha512-Trz4v0+XWlwy68LJIyw3bLbsJiC8XAbRCKF9DbEtZjyndKOGVx6n+wNB0VfoRmY2LKboQLeniap3xrb6LGSJ8A==} @@ -3359,8 +3367,8 @@ packages: engines: {node: '>= 12'} hasBin: true - material-icon-theme@5.26.0: - resolution: {integrity: sha512-Etrm3gMsgCRfpyqE11dDRqb3epoqxq94Pa1QEk20D7uIImneRhtsTFvhOcbKEQfMzup8IBUI1+oYrkfwIb6Xuw==} + material-icon-theme@5.27.0: + resolution: {integrity: sha512-zB3LtPY2n6z4Kp5fSc7OT8KwAZIJRvcOq6PZR4jmAkvmW1Ot+y6lNJmNqERK9rYAT42tZD1YmkhHaMcA2KEObA==} engines: {vscode: ^1.55.0} mathml-tag-names@2.1.3: @@ -3476,10 +3484,6 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} - mini-css-extract-plugin@2.9.4: resolution: {integrity: sha512-ZWYT7ln73Hptxqxk2DxPU9MmapXRhxkJD6tkSR04dnQxm8BGu2hzgKLugK5yySD97u/8yy7Ma7E76k9ZdvtjkQ==} engines: {node: '>= 12.13.0'} @@ -3513,8 +3517,8 @@ packages: monaco-editor: '>= 0.31.0' webpack: ^4.5.0 || 5.x - monaco-editor@0.52.2: - resolution: {integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==} + monaco-editor@0.53.0: + resolution: {integrity: sha512-0WNThgC6CMWNXXBxTbaYYcunj08iB5rnx4/G56UOPeL9UVIUGGHA1GR0EWIh9Ebabj7NpCRawQ5b0hfN1jQmYQ==} moo@0.5.2: resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} @@ -3581,9 +3585,6 @@ packages: engines: {node: '>=12.4.0'} hasBin: true - normalize-package-data@2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -3591,9 +3592,9 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - obj-props@1.4.0: - resolution: {integrity: sha512-p7p/7ltzPDiBs6DqxOrIbtRdwxxVRBj5ROukeNb9RgA+fawhrz5n2hpNz8DDmYR//tviJSj7nUnlppGmONkjiQ==} - engines: {node: '>=0.10.0'} + obj-props@2.0.0: + resolution: {integrity: sha512-Q/uLAAfjdhrzQWN2czRNh3fDCgXjh7yRIkdHjDgIHTwpFP0BsshxTA3HRNffHR7Iw/XGTH30u8vdMXQ+079urA==} + engines: {node: '>=18.0.0'} object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} @@ -3820,12 +3821,6 @@ packages: peerDependencies: postcss: ^8.2.14 - postcss-nesting@13.0.2: - resolution: {integrity: sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==} - engines: {node: '>=18'} - peerDependencies: - postcss: ^8.4 - postcss-resolve-nested-selector@0.1.6: resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==} @@ -3882,9 +3877,9 @@ packages: printable-characters@1.0.42: resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} - proto-props@2.0.0: - resolution: {integrity: sha512-2yma2tog9VaRZY2mn3Wq51uiSW4NcPYT1cQdBagwyrznrilKSZwIZ0UG3ZPL/mx+axEns0hE35T5ufOYZXEnBQ==} - engines: {node: '>=4'} + prototype-properties@5.0.0: + resolution: {integrity: sha512-uCWE2QqnGlwvvJXTwiHTPTyHE62+zORO5hpFWhAwBGDtEtTmNZZleNLJDoFsqHCL4p/CeAP2Q1uMKFUKALuRGQ==} + engines: {node: '>=18.20'} punycode.js@2.3.1: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} @@ -3906,14 +3901,6 @@ packages: read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} - read-pkg-up@7.0.1: - resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} - engines: {node: '>=8'} - - read-pkg@5.2.0: - resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} - engines: {node: '>=8'} - readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -3934,8 +3921,8 @@ packages: resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} hasBin: true - regjsparser@0.10.0: - resolution: {integrity: sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==} + regjsparser@0.12.0: + resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} hasBin: true rename-keys@1.2.0: @@ -3978,11 +3965,6 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} @@ -4026,10 +4008,6 @@ packages: resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} engines: {node: ^14.0.0 || >=16.0.0} - semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} - hasBin: true - semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -4122,9 +4100,6 @@ packages: spdx-compare@1.0.0: resolution: {integrity: sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==} - spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} - spdx-exceptions@2.5.0: resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} @@ -4172,17 +4147,17 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-indent@3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} + strip-indent@4.1.0: + resolution: {integrity: sha512-OA95x+JPmL7kc7zCu+e+TeYxEiaIyndRx0OrBcK2QPPH09oAndr2ALvymxWA+Lx1PYYvFUm4O63pRkdJAaW96w==} + engines: {node: '>=12'} strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} @@ -4268,8 +4243,8 @@ packages: svgson@5.3.1: resolution: {integrity: sha512-qdPgvUNWb40gWktBJnbJRelWcPzkLed/ShhnRsjbayXz8OtdPOzbil9jtiZdrYvSDumAz/VNQr6JaNfPx/gvPA==} - swagger-ui-dist@5.28.1: - resolution: {integrity: sha512-IvPrtNi8MvjiuDgoSmPYgg27Lvu38fnLD1OSd8Y103xXsPAqezVNnNeHnVCZ/d+CMXJblflGaIyHxAYIF3O71w==} + swagger-ui-dist@5.29.0: + resolution: {integrity: sha512-gqs7Md3AxP4mbpXAq31o5QW+wGUZsUzVatg70yXpUR245dfIis5jAzufBd+UQM/w2xSfrhvA1eqsrgnl2PbezQ==} sync-fetch@0.4.5: resolution: {integrity: sha512-esiWJ7ixSKGpd9DJPBTC4ckChqdOjIwJfYhVHkcQ2Gnm41323p1TRmEI+esTQ9ppD+b5opps2OTEGTCGX5kF+g==} @@ -4404,17 +4379,12 @@ packages: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} - type-fest@0.6.0: - resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} - engines: {node: '>=8'} - - type-fest@0.8.1: - resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} - engines: {node: '>=8'} - - type-fest@4.41.0: - resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} - engines: {node: '>=16'} + typescript-eslint@8.43.0: + resolution: {integrity: sha512-FyRGJKUGvcFekRRcBKFBlAhnp4Ng8rhe8tuvvkR9OiU0gfd4vyvTRQHEckO6VDlH57jbeUQem2IpqPq9kLJH+w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' typescript@5.9.2: resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} @@ -4452,8 +4422,8 @@ packages: peerDependencies: browserslist: '>= 4.21.0' - updates@16.6.4: - resolution: {integrity: sha512-HHJH9SBWJr/1+iSLuS1txaq4bz2q/ENK4KHRhcTgOgJEUeBnmLeF5/0nQya9vGnRGLnrXacuAk+pTAzKuOs9ng==} + updates@16.7.0: + resolution: {integrity: sha512-GOqKdZREAlOR90YBBY8FpkmJOPjjxntYcuAKZRWXK5Pvt2CAcPREk+KeqClG3yqqk+NGWkqAsIxa1ORzQB1iQA==} engines: {node: '>=20'} hasBin: true @@ -4474,9 +4444,6 @@ packages: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true - validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - validate-npm-package-name@3.0.0: resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==} @@ -4491,8 +4458,8 @@ packages: vite-string-plugin@1.4.6: resolution: {integrity: sha512-Csjtny8/uVIynzlaRRj4RpHrPAakNwlH9jw6kgQ8tQhc2f0zzA6bCbAgWD0y84EgB8aLNrz7pZFUqSt3LOtk+w==} - vite@7.1.4: - resolution: {integrity: sha512-X5QFK4SGynAeeIt+A7ZWnApdUyHYm+pzv/8/A57LqSGcI88U6R6ipOs3uCesdc6yl7nl+zNO0t8LmqAdXcQihw==} + vite@7.1.5: + resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -4705,8 +4672,8 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} - wrap-ansi@9.0.0: - resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} engines: {node: '>=18'} wrappy@1.0.2: @@ -4871,10 +4838,6 @@ snapshots: '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 - '@csstools/selector-resolve-nested@3.1.0(postcss-selector-parser@7.1.0)': - dependencies: - postcss-selector-parser: 7.1.0 - '@csstools/selector-specificity@5.0.0(postcss-selector-parser@7.1.0)': dependencies: postcss-selector-parser: 7.1.0 @@ -4977,25 +4940,43 @@ snapshots: '@esbuild/win32-x64@0.25.9': optional: true - '@eslint-community/eslint-plugin-eslint-comments@4.5.0(eslint@8.57.0)': + '@eslint-community/eslint-plugin-eslint-comments@4.5.0(eslint@9.35.0(jiti@2.5.1))': dependencies: escape-string-regexp: 4.0.0 - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) ignore: 5.3.2 - '@eslint-community/eslint-utils@4.8.0(eslint@8.57.0)': + '@eslint-community/eslint-utils@4.9.0(eslint@9.35.0(jiti@2.5.1))': dependencies: - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} - '@eslint/eslintrc@2.1.4': + '@eslint/compat@1.3.2(eslint@9.35.0(jiti@2.5.1))': + optionalDependencies: + eslint: 9.35.0(jiti@2.5.1) + + '@eslint/config-array@0.21.0': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.1 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.3.1': {} + + '@eslint/core@0.15.2': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 debug: 4.4.1 - espree: 9.6.1 - globals: 13.24.0 + espree: 10.4.0 + globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 js-yaml: 4.1.0 @@ -5004,7 +4985,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@8.57.0': {} + '@eslint/js@9.35.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.3.5': + dependencies: + '@eslint/core': 0.15.2 + levn: 0.4.1 '@github/browserslist-config@1.0.0': {} @@ -5012,6 +5000,8 @@ snapshots: '@github/markdown-toolbar-element@2.2.3': {} + '@github/paste-markdown@1.5.3': {} + '@github/relative-time-element@4.4.8': {} '@github/text-expander-element@2.9.2': @@ -5019,17 +5009,16 @@ snapshots: '@github/combobox-nav': 2.3.1 dom-input-range: 2.0.1 - '@humanwhocodes/config-array@0.11.14': + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.1 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/object-schema@2.0.3': {} + '@humanwhocodes/retry@0.4.3': {} '@iconify/types@2.0.0': {} @@ -5056,7 +5045,7 @@ snapshots: dependencies: string-width: 5.1.2 string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 @@ -5192,7 +5181,7 @@ snapshots: '@popperjs/core@2.11.8': {} - '@primer/octicons@19.17.0': + '@primer/octicons@19.18.0': dependencies: object-assign: 4.1.1 @@ -5549,11 +5538,15 @@ snapshots: '@stoplight/yaml-ast-parser': 0.0.50 tslib: 2.8.1 - '@stylistic/eslint-plugin-js@3.1.0(eslint@8.57.0)': + '@stylistic/eslint-plugin@5.3.1(eslint@9.35.0(jiti@2.5.1))': dependencies: - eslint: 8.57.0 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.5.1)) + '@typescript-eslint/types': 8.43.0 + eslint: 9.35.0(jiti@2.5.1) eslint-visitor-keys: 4.2.1 espree: 10.4.0 + estraverse: 5.3.0 + picomatch: 4.0.3 '@stylistic/stylelint-plugin@4.0.0(stylelint@16.24.0(typescript@5.9.2))': dependencies: @@ -5753,10 +5746,6 @@ snapshots: '@types/katex@0.16.7': {} - '@types/license-checker-webpack-plugin@0.2.5': - dependencies: - '@types/webpack': 4.41.40 - '@types/markdown-escape@1.1.3': {} '@types/marked@4.3.2': {} @@ -5771,8 +5760,6 @@ snapshots: dependencies: undici-types: 7.10.0 - '@types/normalize-package-data@2.4.4': {} - '@types/pdfobject@2.2.5': {} '@types/sarif@2.1.7': {} @@ -5781,12 +5768,8 @@ snapshots: '@types/sortablejs@1.15.8': {} - '@types/source-list-map@0.1.6': {} - '@types/swagger-ui-dist@3.30.6': {} - '@types/tapable@1.0.12': {} - '@types/tern@0.23.9': dependencies: '@types/estree': 1.0.8 @@ -5797,43 +5780,26 @@ snapshots: '@types/toastify-js@1.12.4': {} + '@types/trusted-types@1.0.6': {} + '@types/trusted-types@2.0.7': optional: true - '@types/uglify-js@3.17.5': - dependencies: - source-map: 0.6.1 - '@types/unist@2.0.11': {} '@types/urijs@1.19.25': {} - '@types/webpack-sources@3.2.3': - dependencies: - '@types/node': 24.3.1 - '@types/source-list-map': 0.1.6 - source-map: 0.7.6 - - '@types/webpack@4.41.40': - dependencies: - '@types/node': 24.3.1 - '@types/tapable': 1.0.12 - '@types/uglify-js': 3.17.5 - '@types/webpack-sources': 3.2.3 - anymatch: 3.1.3 - source-map: 0.6.1 - '@types/whatwg-mimetype@3.0.2': {} - '@typescript-eslint/eslint-plugin@8.42.0(@typescript-eslint/parser@8.42.0(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0)(typescript@5.9.2)': + '@typescript-eslint/eslint-plugin@8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.42.0(eslint@8.57.0)(typescript@5.9.2) - '@typescript-eslint/scope-manager': 8.42.0 - '@typescript-eslint/type-utils': 8.42.0(eslint@8.57.0)(typescript@5.9.2) - '@typescript-eslint/utils': 8.42.0(eslint@8.57.0)(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 8.42.0 - eslint: 8.57.0 + '@typescript-eslint/parser': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.43.0 + '@typescript-eslint/type-utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.43.0 + eslint: 9.35.0(jiti@2.5.1) graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 @@ -5842,56 +5808,56 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.42.0(eslint@8.57.0)(typescript@5.9.2)': + '@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)': dependencies: - '@typescript-eslint/scope-manager': 8.42.0 - '@typescript-eslint/types': 8.42.0 - '@typescript-eslint/typescript-estree': 8.42.0(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 8.42.0 + '@typescript-eslint/scope-manager': 8.43.0 + '@typescript-eslint/types': 8.43.0 + '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.43.0 debug: 4.4.1 - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.42.0(typescript@5.9.2)': + '@typescript-eslint/project-service@8.43.0(typescript@5.9.2)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.42.0(typescript@5.9.2) - '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/tsconfig-utils': 8.43.0(typescript@5.9.2) + '@typescript-eslint/types': 8.43.0 debug: 4.4.1 typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.42.0': + '@typescript-eslint/scope-manager@8.43.0': dependencies: - '@typescript-eslint/types': 8.42.0 - '@typescript-eslint/visitor-keys': 8.42.0 + '@typescript-eslint/types': 8.43.0 + '@typescript-eslint/visitor-keys': 8.43.0 - '@typescript-eslint/tsconfig-utils@8.42.0(typescript@5.9.2)': + '@typescript-eslint/tsconfig-utils@8.43.0(typescript@5.9.2)': dependencies: typescript: 5.9.2 - '@typescript-eslint/type-utils@8.42.0(eslint@8.57.0)(typescript@5.9.2)': + '@typescript-eslint/type-utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)': dependencies: - '@typescript-eslint/types': 8.42.0 - '@typescript-eslint/typescript-estree': 8.42.0(typescript@5.9.2) - '@typescript-eslint/utils': 8.42.0(eslint@8.57.0)(typescript@5.9.2) + '@typescript-eslint/types': 8.43.0 + '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.9.2) + '@typescript-eslint/utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) debug: 4.4.1 - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) ts-api-utils: 2.1.0(typescript@5.9.2) typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.42.0': {} + '@typescript-eslint/types@8.43.0': {} - '@typescript-eslint/typescript-estree@8.42.0(typescript@5.9.2)': + '@typescript-eslint/typescript-estree@8.43.0(typescript@5.9.2)': dependencies: - '@typescript-eslint/project-service': 8.42.0(typescript@5.9.2) - '@typescript-eslint/tsconfig-utils': 8.42.0(typescript@5.9.2) - '@typescript-eslint/types': 8.42.0 - '@typescript-eslint/visitor-keys': 8.42.0 + '@typescript-eslint/project-service': 8.43.0(typescript@5.9.2) + '@typescript-eslint/tsconfig-utils': 8.43.0(typescript@5.9.2) + '@typescript-eslint/types': 8.43.0 + '@typescript-eslint/visitor-keys': 8.43.0 debug: 4.4.1 fast-glob: 3.3.3 is-glob: 4.0.3 @@ -5902,24 +5868,22 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.42.0(eslint@8.57.0)(typescript@5.9.2)': + '@typescript-eslint/utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)': dependencies: - '@eslint-community/eslint-utils': 4.8.0(eslint@8.57.0) - '@typescript-eslint/scope-manager': 8.42.0 - '@typescript-eslint/types': 8.42.0 - '@typescript-eslint/typescript-estree': 8.42.0(typescript@5.9.2) - eslint: 8.57.0 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.5.1)) + '@typescript-eslint/scope-manager': 8.43.0 + '@typescript-eslint/types': 8.43.0 + '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.9.2) + eslint: 9.35.0(jiti@2.5.1) typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.42.0': + '@typescript-eslint/visitor-keys@8.43.0': dependencies: - '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/types': 8.43.0 eslint-visitor-keys: 4.2.1 - '@ungap/structured-clone@1.3.0': {} - '@unrs/resolver-binding-android-arm-eabi@1.11.1': optional: true @@ -5979,17 +5943,17 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vitejs/plugin-vue@6.0.1(vite@7.1.4(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2))': + '@vitejs/plugin-vue@6.0.1(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.21(typescript@5.9.2))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.29 - vite: 7.1.4(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1) vue: 3.5.21(typescript@5.9.2) - '@vitest/eslint-plugin@1.3.9(eslint@8.57.0)(typescript@5.9.2)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.1)(happy-dom@18.0.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1))': + '@vitest/eslint-plugin@1.3.9(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.3.1)(happy-dom@18.0.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1))': dependencies: - '@typescript-eslint/scope-manager': 8.42.0 - '@typescript-eslint/utils': 8.42.0(eslint@8.57.0)(typescript@5.9.2) - eslint: 8.57.0 + '@typescript-eslint/scope-manager': 8.43.0 + '@typescript-eslint/utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + eslint: 9.35.0(jiti@2.5.1) optionalDependencies: typescript: 5.9.2 vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.3.1)(happy-dom@18.0.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1) @@ -6004,13 +5968,13 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.1.4(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1))': + '@vitest/mocker@3.2.4(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 - magic-string: 0.30.18 + magic-string: 0.30.19 optionalDependencies: - vite: 7.1.4(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1) '@vitest/pretty-format@3.2.4': dependencies: @@ -6025,7 +5989,7 @@ snapshots: '@vitest/snapshot@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 - magic-string: 0.30.18 + magic-string: 0.30.19 pathe: 2.0.3 '@vitest/spy@3.2.4': @@ -6071,7 +6035,7 @@ snapshots: '@vue/compiler-ssr': 3.5.21 '@vue/shared': 3.5.21 estree-walker: 2.0.2 - magic-string: 0.30.18 + magic-string: 0.30.19 postcss: 8.5.6 source-map-js: 1.2.1 @@ -6231,8 +6195,8 @@ snapshots: acorn@8.15.0: {} - add-asset-webpack-plugin@3.0.0(webpack@5.101.3): - dependencies: + add-asset-webpack-plugin@3.1.1(webpack@5.101.3): + optionalDependencies: webpack: 5.101.3(webpack-cli@6.0.1) ajv-draft-04@1.0.0(ajv@8.17.1): @@ -6270,13 +6234,13 @@ snapshots: ansi-regex@5.0.1: {} - ansi-regex@6.2.0: {} + ansi-regex@6.2.2: {} ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.1: {} + ansi-styles@6.2.3: {} ansi_up@6.0.6: {} @@ -6352,7 +6316,7 @@ snapshots: browserslist@4.25.4: dependencies: caniuse-lite: 1.0.30001741 - electron-to-chromium: 1.5.214 + electron-to-chromium: 1.5.215 node-releases: 2.0.20 update-browserslist-db: 1.1.3(browserslist@4.25.4) @@ -6365,6 +6329,8 @@ snapshots: builtin-modules@3.3.0: {} + builtin-modules@5.0.0: {} + builtins@1.0.3: {} bytes@3.1.2: {} @@ -6395,6 +6361,8 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + change-case@5.4.4: {} + character-entities-legacy@3.0.0: {} character-entities@2.0.2: {} @@ -6837,10 +6805,6 @@ snapshots: dependencies: esutils: 2.0.3 - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 - dom-input-range@2.0.1: {} dom-serializer@2.0.0: @@ -6880,7 +6844,7 @@ snapshots: codemirror-spell-checker: 1.1.2 marked: 4.3.0 - electron-to-chromium@1.5.214: {} + electron-to-chromium@1.5.215: {} emoji-regex@10.5.0: {} @@ -6950,14 +6914,14 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-compat-utils@0.6.5(eslint@8.57.0): + eslint-compat-utils@0.6.5(eslint@9.35.0(jiti@2.5.1)): dependencies: - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) semver: 7.7.2 - eslint-config-prettier@10.1.8(eslint@8.57.0): + eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.5.1)): dependencies: - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) eslint-import-context@0.1.9(unrs-resolver@1.11.1): dependencies: @@ -6974,10 +6938,10 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.42.0(eslint@8.57.0)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0))(eslint-plugin-import@2.32.0)(eslint@8.57.0): + eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.35.0(jiti@2.5.1)))(eslint-plugin-import@2.32.0)(eslint@9.35.0(jiti@2.5.1)): dependencies: debug: 4.4.1 - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) eslint-import-context: 0.1.9(unrs-resolver@1.11.1) get-tsconfig: 4.10.1 is-bun-module: 2.0.0 @@ -6985,82 +6949,87 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.42.0(eslint@8.57.0)(typescript@5.9.2))(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.0) - eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.42.0(eslint@8.57.0)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@4.4.4)(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.35.0(jiti@2.5.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.42.0(eslint@8.57.0)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.0): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.35.0(jiti@2.5.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.42.0(eslint@8.57.0)(typescript@5.9.2) - eslint: 8.57.0 + '@typescript-eslint/parser': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + eslint: 9.35.0(jiti@2.5.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.42.0(eslint@8.57.0)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0))(eslint-plugin-import@2.32.0)(eslint@8.57.0) + eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.35.0(jiti@2.5.1)))(eslint-plugin-import@2.32.0)(eslint@9.35.0(jiti@2.5.1)) transitivePeerDependencies: - supports-color - eslint-plugin-array-func@4.0.0(eslint@8.57.0): + eslint-plugin-array-func@5.0.2(eslint@9.35.0(jiti@2.5.1)): dependencies: - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) - eslint-plugin-escompat@3.11.4(eslint@8.57.0): + eslint-plugin-escompat@3.11.4(eslint@9.35.0(jiti@2.5.1)): dependencies: browserslist: 4.25.4 - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) - eslint-plugin-eslint-comments@3.2.0(eslint@8.57.0): + eslint-plugin-eslint-comments@3.2.0(eslint@9.35.0(jiti@2.5.1)): dependencies: escape-string-regexp: 1.0.5 - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) ignore: 5.3.2 - eslint-plugin-filenames@1.3.2(eslint@8.57.0): + eslint-plugin-filenames@1.3.2(eslint@9.35.0(jiti@2.5.1)): dependencies: - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) lodash.camelcase: 4.3.0 lodash.kebabcase: 4.1.1 lodash.snakecase: 4.1.1 lodash.upperfirst: 4.3.1 - eslint-plugin-github@5.0.2(@types/eslint@9.6.1)(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.0)(typescript@5.9.2): + eslint-plugin-github@6.0.0(@types/eslint@9.6.1)(eslint-import-resolver-typescript@4.4.4)(eslint@9.35.0(jiti@2.5.1)): dependencies: + '@eslint/compat': 1.3.2(eslint@9.35.0(jiti@2.5.1)) + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.35.0 '@github/browserslist-config': 1.0.0 - '@typescript-eslint/eslint-plugin': 8.42.0(@typescript-eslint/parser@8.42.0(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0)(typescript@5.9.2) - '@typescript-eslint/parser': 8.42.0(eslint@8.57.0)(typescript@5.9.2) + '@typescript-eslint/eslint-plugin': 8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/parser': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) aria-query: 5.3.2 - eslint: 8.57.0 - eslint-config-prettier: 10.1.8(eslint@8.57.0) - eslint-plugin-escompat: 3.11.4(eslint@8.57.0) - eslint-plugin-eslint-comments: 3.2.0(eslint@8.57.0) - eslint-plugin-filenames: 1.3.2(eslint@8.57.0) - eslint-plugin-i18n-text: 1.0.1(eslint@8.57.0) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.42.0(eslint@8.57.0)(typescript@5.9.2))(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.0) - eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.0) + eslint: 9.35.0(jiti@2.5.1) + eslint-config-prettier: 10.1.8(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-escompat: 3.11.4(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-eslint-comments: 3.2.0(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-filenames: 1.3.2(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-i18n-text: 1.0.1(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@4.4.4)(eslint@9.35.0(jiti@2.5.1)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.35.0(jiti@2.5.1)) eslint-plugin-no-only-tests: 3.3.0 - eslint-plugin-prettier: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@8.57.0))(eslint@8.57.0)(prettier@3.6.2) + eslint-plugin-prettier: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1))(prettier@3.6.2) eslint-rule-documentation: 1.0.23 + globals: 16.4.0 jsx-ast-utils: 3.3.5 prettier: 3.6.2 svg-element-attributes: 1.3.1 + typescript: 5.9.2 + typescript-eslint: 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) transitivePeerDependencies: - '@types/eslint' - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - - typescript - eslint-plugin-i18n-text@1.0.1(eslint@8.57.0): + eslint-plugin-i18n-text@1.0.1(eslint@9.35.0(jiti@2.5.1)): dependencies: - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) - eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.42.0(eslint@8.57.0)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): + eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint@9.35.0(jiti@2.5.1)): dependencies: - '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/types': 8.43.0 comment-parser: 1.4.1 debug: 4.4.1 - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) eslint-import-context: 0.1.9(unrs-resolver@1.11.1) is-glob: 4.0.3 minimatch: 10.0.3 @@ -7068,12 +7037,12 @@ snapshots: stable-hash-x: 0.2.0 unrs-resolver: 1.11.1 optionalDependencies: - '@typescript-eslint/utils': 8.42.0(eslint@8.57.0)(typescript@5.9.2) + '@typescript-eslint/utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.42.0(eslint@8.57.0)(typescript@5.9.2))(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.0): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-typescript@4.4.4)(eslint@9.35.0(jiti@2.5.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: '@nolyfill/array-includes@1.0.44' @@ -7082,9 +7051,9 @@ snapshots: array.prototype.flatmap: '@nolyfill/array.prototype.flatmap@1.0.44' debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.42.0(eslint@8.57.0)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@8.57.0) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.35.0(jiti@2.5.1)) hasown: '@nolyfill/hasown@1.0.44' is-core-module: '@nolyfill/is-core-module@1.0.39' is-glob: 4.0.3 @@ -7096,13 +7065,13 @@ snapshots: string.prototype.trimend: '@nolyfill/string.prototype.trimend@1.0.44' tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.42.0(eslint@8.57.0)(typescript@5.9.2) + '@typescript-eslint/parser': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.0): + eslint-plugin-jsx-a11y@6.10.2(eslint@9.35.0(jiti@2.5.1)): dependencies: aria-query: 5.3.2 array-includes: '@nolyfill/array-includes@1.0.44' @@ -7112,7 +7081,7 @@ snapshots: axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) hasown: '@nolyfill/hasown@1.0.44' jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -7121,51 +7090,48 @@ snapshots: safe-regex-test: '@nolyfill/safe-regex-test@1.0.44' string.prototype.includes: '@nolyfill/string.prototype.includes@1.0.44' - eslint-plugin-no-jquery@3.1.1(eslint@8.57.0): - dependencies: - eslint: 8.57.0 - eslint-plugin-no-only-tests@3.3.0: {} - eslint-plugin-no-use-extend-native@0.5.0: + eslint-plugin-no-use-extend-native@0.7.2(eslint@9.35.0(jiti@2.5.1)): dependencies: - is-get-set-prop: 1.0.0 - is-js-type: 2.0.0 - is-obj-prop: 1.0.0 - is-proto-prop: 2.0.0 + eslint: 9.35.0(jiti@2.5.1) + is-get-set-prop: 2.0.0 + is-js-type: 3.0.0 + is-obj-prop: 2.0.0 + is-proto-prop: 3.0.1 - eslint-plugin-playwright@2.2.2(eslint@8.57.0): + eslint-plugin-playwright@2.2.2(eslint@9.35.0(jiti@2.5.1)): dependencies: - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) globals: 13.24.0 - eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@8.57.0))(eslint@8.57.0)(prettier@3.6.2): + eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.35.0(jiti@2.5.1)))(eslint@9.35.0(jiti@2.5.1))(prettier@3.6.2): dependencies: - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) prettier: 3.6.2 prettier-linter-helpers: 1.0.0 synckit: 0.11.11 optionalDependencies: '@types/eslint': 9.6.1 - eslint-config-prettier: 10.1.8(eslint@8.57.0) + eslint-config-prettier: 10.1.8(eslint@9.35.0(jiti@2.5.1)) - eslint-plugin-regexp@2.10.0(eslint@8.57.0): + eslint-plugin-regexp@2.10.0(eslint@9.35.0(jiti@2.5.1)): dependencies: - '@eslint-community/eslint-utils': 4.8.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.5.1)) '@eslint-community/regexpp': 4.12.1 comment-parser: 1.4.1 - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) jsdoc-type-pratt-parser: 4.8.0 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 - eslint-plugin-sonarjs@3.0.5(eslint@8.57.0): + eslint-plugin-sonarjs@3.0.5(eslint@9.35.0(jiti@2.5.1)): dependencies: '@eslint-community/regexpp': 4.12.1 builtin-modules: 3.3.0 bytes: 3.1.2 - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) functional-red-black-tree: 1.0.1 jsx-ast-utils-x: 0.1.0 lodash.merge: 4.6.2 @@ -7174,57 +7140,59 @@ snapshots: semver: 7.7.2 typescript: 5.9.2 - eslint-plugin-unicorn@56.0.1(eslint@8.57.0): + eslint-plugin-unicorn@61.0.2(eslint@9.35.0(jiti@2.5.1)): dependencies: '@babel/helper-validator-identifier': 7.27.1 - '@eslint-community/eslint-utils': 4.8.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.5.1)) + '@eslint/plugin-kit': 0.3.5 + change-case: 5.4.4 ci-info: 4.3.0 clean-regexp: 1.0.0 core-js-compat: 3.45.1 - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) esquery: 1.6.0 - globals: 15.15.0 - indent-string: 4.0.0 - is-builtin-module: 3.2.1 + find-up-simple: 1.0.1 + globals: 16.4.0 + indent-string: 5.0.0 + is-builtin-module: 5.0.0 jsesc: 3.1.0 pluralize: 8.0.0 - read-pkg-up: 7.0.1 regexp-tree: 0.1.27 - regjsparser: 0.10.0 + regjsparser: 0.12.0 semver: 7.7.2 - strip-indent: 3.0.0 + strip-indent: 4.1.0 - eslint-plugin-vue-scoped-css@2.12.0(eslint@8.57.0)(vue-eslint-parser@10.2.0(eslint@8.57.0)): + eslint-plugin-vue-scoped-css@2.12.0(eslint@9.35.0(jiti@2.5.1))(vue-eslint-parser@10.2.0(eslint@9.35.0(jiti@2.5.1))): dependencies: - '@eslint-community/eslint-utils': 4.8.0(eslint@8.57.0) - eslint: 8.57.0 - eslint-compat-utils: 0.6.5(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.5.1)) + eslint: 9.35.0(jiti@2.5.1) + eslint-compat-utils: 0.6.5(eslint@9.35.0(jiti@2.5.1)) lodash: 4.17.21 postcss: 8.5.6 postcss-safe-parser: 6.0.0(postcss@8.5.6) postcss-scss: 4.0.9(postcss@8.5.6) postcss-selector-parser: 7.1.0 postcss-styl: 0.12.3 - vue-eslint-parser: 10.2.0(eslint@8.57.0) + vue-eslint-parser: 10.2.0(eslint@9.35.0(jiti@2.5.1)) transitivePeerDependencies: - supports-color - eslint-plugin-vue@10.4.0(@typescript-eslint/parser@8.42.0(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0)(vue-eslint-parser@10.2.0(eslint@8.57.0)): + eslint-plugin-vue@10.4.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1))(vue-eslint-parser@10.2.0(eslint@9.35.0(jiti@2.5.1))): dependencies: - '@eslint-community/eslint-utils': 4.8.0(eslint@8.57.0) - eslint: 8.57.0 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.5.1)) + eslint: 9.35.0(jiti@2.5.1) natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 6.1.2 semver: 7.7.2 - vue-eslint-parser: 10.2.0(eslint@8.57.0) + vue-eslint-parser: 10.2.0(eslint@9.35.0(jiti@2.5.1)) xml-name-validator: 4.0.0 optionalDependencies: - '@typescript-eslint/parser': 8.42.0(eslint@8.57.0)(typescript@5.9.2) + '@typescript-eslint/parser': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) - eslint-plugin-wc@3.0.1(eslint@8.57.0): + eslint-plugin-wc@3.0.1(eslint@9.35.0(jiti@2.5.1)): dependencies: - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) is-valid-element-name: 1.0.0 js-levenshtein-esm: 2.0.0 @@ -7235,11 +7203,6 @@ snapshots: esrecurse: 4.3.0 estraverse: 4.3.0 - eslint-scope@7.2.2: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 @@ -7249,46 +7212,45 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@8.57.0: + eslint@9.35.0(jiti@2.5.1): dependencies: - '@eslint-community/eslint-utils': 4.8.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.35.0(jiti@2.5.1)) '@eslint-community/regexpp': 4.12.1 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.0 - '@humanwhocodes/config-array': 0.11.14 + '@eslint/config-array': 0.21.0 + '@eslint/config-helpers': 0.3.1 + '@eslint/core': 0.15.2 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.35.0 + '@eslint/plugin-kit': 0.3.5 + '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.3.0 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.1 - doctrine: 3.0.0 escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 + file-entry-cache: 8.0.0 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 ignore: 5.3.2 imurmurhash: 0.1.4 is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.0 json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 lodash.merge: 4.6.2 minimatch: 3.1.2 natural-compare: 1.4.0 optionator: 0.9.4 - strip-ansi: 6.0.1 - text-table: 0.2.0 + optionalDependencies: + jiti: 2.5.1 transitivePeerDependencies: - supports-color @@ -7298,12 +7260,6 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 4.2.1 - espree@9.6.1: - dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - eslint-visitor-keys: 3.4.3 - esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -7386,14 +7342,16 @@ snapshots: dependencies: flat-cache: 6.1.13 - file-entry-cache@6.0.1: + file-entry-cache@8.0.0: dependencies: - flat-cache: 3.2.0 + flat-cache: 4.0.1 fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 + find-up-simple@1.0.1: {} + find-up@4.1.0: dependencies: locate-path: 5.0.0 @@ -7404,11 +7362,10 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - flat-cache@3.2.0: + flat-cache@4.0.1: dependencies: flatted: 3.3.3 keyv: 4.5.4 - rimraf: 3.0.2 flat-cache@6.1.13: dependencies: @@ -7443,9 +7400,9 @@ snapshots: get-caller-file@2.0.5: {} - get-east-asian-width@1.3.1: {} + get-east-asian-width@1.4.0: {} - get-set-props@0.1.0: {} + get-set-props@0.2.0: {} get-source@2.0.12: dependencies: @@ -7507,8 +7464,12 @@ snapshots: dependencies: type-fest: 0.20.2 + globals@14.0.0: {} + globals@15.15.0: {} + globals@16.4.0: {} + globby@11.1.0: dependencies: array-union: 2.1.0 @@ -7542,8 +7503,6 @@ snapshots: hookified@1.12.0: {} - hosted-git-info@2.8.9: {} - hpagent@1.2.0: {} html-tags@3.3.1: {} @@ -7555,7 +7514,7 @@ snapshots: domutils: 3.2.2 entities: 4.5.0 - htmx.org@2.0.6: {} + htmx.org@2.0.7: {} iconv-lite@0.6.3: dependencies: @@ -7587,7 +7546,7 @@ snapshots: imurmurhash@0.1.4: {} - indent-string@4.0.0: {} + indent-string@5.0.0: {} inflight@1.0.6: dependencies: @@ -7621,9 +7580,9 @@ snapshots: is-buffer@1.1.6: {} - is-builtin-module@3.2.1: + is-builtin-module@5.0.0: dependencies: - builtin-modules: 3.3.0 + builtin-modules: 5.0.0 is-bun-module@2.0.0: dependencies: @@ -7635,10 +7594,10 @@ snapshots: is-fullwidth-code-point@3.0.0: {} - is-get-set-prop@1.0.0: + is-get-set-prop@2.0.0: dependencies: - get-set-props: 0.1.0 - lowercase-keys: 1.0.1 + get-set-props: 0.2.0 + lowercase-keys: 3.0.0 is-glob@4.0.3: dependencies: @@ -7646,18 +7605,16 @@ snapshots: is-hexadecimal@2.0.1: {} - is-js-type@2.0.0: + is-js-type@3.0.0: dependencies: - js-types: 1.0.0 + js-types: 4.0.0 is-number@7.0.0: {} - is-obj-prop@1.0.0: + is-obj-prop@2.0.0: dependencies: - lowercase-keys: 1.0.1 - obj-props: 1.4.0 - - is-path-inside@3.0.3: {} + lowercase-keys: 3.0.0 + obj-props: 2.0.0 is-plain-object@2.0.4: dependencies: @@ -7667,10 +7624,10 @@ snapshots: is-potential-custom-element-name@1.0.1: {} - is-proto-prop@2.0.0: + is-proto-prop@3.0.1: dependencies: - lowercase-keys: 1.0.1 - proto-props: 2.0.0 + lowercase-keys: 3.0.0 + prototype-properties: 5.0.0 is-reference@1.2.1: dependencies: @@ -7712,7 +7669,7 @@ snapshots: js-tokens@9.0.1: {} - js-types@1.0.0: {} + js-types@4.0.0: {} js-yaml@4.1.0: dependencies: @@ -7722,7 +7679,7 @@ snapshots: jsep@1.4.0: {} - jsesc@0.5.0: {} + jsesc@3.0.2: {} jsesc@3.1.0: {} @@ -7872,7 +7829,7 @@ snapshots: loupe@3.2.1: {} - lowercase-keys@1.0.1: {} + lowercase-keys@3.0.0: {} lru-cache@10.4.3: {} @@ -7882,7 +7839,7 @@ snapshots: dependencies: sourcemap-codec: 1.4.8 - magic-string@0.30.18: + magic-string@0.30.19: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -7930,7 +7887,7 @@ snapshots: marked@4.3.0: {} - material-icon-theme@5.26.0: + material-icon-theme@5.27.0: dependencies: chroma-js: 3.1.2 events: 3.3.0 @@ -8159,8 +8116,6 @@ snapshots: dependencies: mime-db: 1.52.0 - min-indent@1.0.1: {} - mini-css-extract-plugin@2.9.4(webpack@5.101.3): dependencies: schema-utils: 4.3.2 @@ -8190,13 +8145,15 @@ snapshots: pkg-types: 1.3.1 ufo: 1.6.1 - monaco-editor-webpack-plugin@7.1.0(monaco-editor@0.52.2)(webpack@5.101.3): + monaco-editor-webpack-plugin@7.1.0(monaco-editor@0.53.0)(webpack@5.101.3): dependencies: loader-utils: 2.0.4 - monaco-editor: 0.52.2 + monaco-editor: 0.53.0 webpack: 5.101.3(webpack-cli@6.0.1) - monaco-editor@0.52.2: {} + monaco-editor@0.53.0: + dependencies: + '@types/trusted-types': 1.0.6 moo@0.5.2: {} @@ -8247,20 +8204,13 @@ snapshots: nolyfill@1.0.44: {} - normalize-package-data@2.5.0: - dependencies: - hosted-git-info: 2.8.9 - resolve: 1.22.10 - semver: 5.7.2 - validate-npm-package-license: 3.0.4 - normalize-path@3.0.0: {} nth-check@2.1.1: dependencies: boolbase: 1.0.0 - obj-props@1.4.0: {} + obj-props@2.0.0: {} object-assign@4.1.1: {} @@ -8468,13 +8418,6 @@ snapshots: postcss: 8.5.6 postcss-selector-parser: 6.1.2 - postcss-nesting@13.0.2(postcss@8.5.6): - dependencies: - '@csstools/selector-resolve-nested': 3.1.0(postcss-selector-parser@7.1.0) - '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.0) - postcss: 8.5.6 - postcss-selector-parser: 7.1.0 - postcss-resolve-nested-selector@0.1.6: {} postcss-safe-parser@6.0.0(postcss@8.5.6): @@ -8527,7 +8470,7 @@ snapshots: printable-characters@1.0.42: {} - proto-props@2.0.0: {} + prototype-properties@5.0.0: {} punycode.js@2.3.1: {} @@ -8545,19 +8488,6 @@ snapshots: dependencies: pify: 2.3.0 - read-pkg-up@7.0.1: - dependencies: - find-up: 4.1.0 - read-pkg: 5.2.0 - type-fest: 0.8.1 - - read-pkg@5.2.0: - dependencies: - '@types/normalize-package-data': 2.4.4 - normalize-package-data: 2.5.0 - parse-json: 5.2.0 - type-fest: 0.6.0 - readdirp@3.6.0: dependencies: picomatch: 2.3.1 @@ -8577,9 +8507,9 @@ snapshots: regexp-tree@0.1.27: {} - regjsparser@0.10.0: + regjsparser@0.12.0: dependencies: - jsesc: 0.5.0 + jsesc: 3.0.2 rename-keys@1.2.0: {} @@ -8607,10 +8537,6 @@ snapshots: reusify@1.1.0: {} - rimraf@3.0.2: - dependencies: - glob: 7.2.3 - robust-predicates@3.0.2: {} rollup@2.79.2: @@ -8683,8 +8609,6 @@ snapshots: refa: 0.12.1 regexp-ast-analysis: 0.7.1 - semver@5.7.2: {} - semver@6.3.1: {} semver@7.7.2: {} @@ -8761,11 +8685,6 @@ snapshots: spdx-expression-parse: 3.0.1 spdx-ranges: 2.1.1 - spdx-correct@3.2.0: - dependencies: - spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.22 - spdx-exceptions@2.5.0: {} spdx-expression-parse@3.0.1: @@ -8808,27 +8727,25 @@ snapshots: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 string-width@7.2.0: dependencies: emoji-regex: 10.5.0 - get-east-asian-width: 1.3.1 - strip-ansi: 7.1.0 + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.2 strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.0: + strip-ansi@7.1.2: dependencies: - ansi-regex: 6.2.0 + ansi-regex: 6.2.2 strip-bom@3.0.0: {} - strip-indent@3.0.0: - dependencies: - min-indent: 1.0.1 + strip-indent@4.1.0: {} strip-json-comments@3.1.1: {} @@ -8959,7 +8876,7 @@ snapshots: deep-rename-keys: 0.2.1 xml-reader: 2.4.3 - swagger-ui-dist@5.28.1: + swagger-ui-dist@5.29.0: dependencies: '@scarf/scarf': 1.4.0 @@ -9099,11 +9016,16 @@ snapshots: type-fest@0.20.2: {} - type-fest@0.6.0: {} - - type-fest@0.8.1: {} - - type-fest@4.41.0: {} + typescript-eslint@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2): + dependencies: + '@typescript-eslint/eslint-plugin': 8.43.0(@typescript-eslint/parser@8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/parser': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.9.2) + '@typescript-eslint/utils': 8.43.0(eslint@9.35.0(jiti@2.5.1))(typescript@5.9.2) + eslint: 9.35.0(jiti@2.5.1) + typescript: 5.9.2 + transitivePeerDependencies: + - supports-color typescript@5.9.2: {} @@ -9151,7 +9073,7 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - updates@16.6.4: {} + updates@16.7.0: {} uri-js@4.4.1: dependencies: @@ -9165,11 +9087,6 @@ snapshots: uuid@11.1.0: {} - validate-npm-package-license@3.0.4: - dependencies: - spdx-correct: 3.2.0 - spdx-expression-parse: 3.0.1 - validate-npm-package-name@3.0.0: dependencies: builtins: 1.0.3 @@ -9182,7 +9099,7 @@ snapshots: debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.4(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -9199,7 +9116,7 @@ snapshots: vite-string-plugin@1.4.6: {} - vite@7.1.4(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1): + vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1): dependencies: esbuild: 0.25.9 fdir: 6.5.0(picomatch@4.0.3) @@ -9219,7 +9136,7 @@ snapshots: dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.4(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -9228,7 +9145,7 @@ snapshots: chai: 5.3.3 debug: 4.4.1 expect-type: 1.2.2 - magic-string: 0.30.18 + magic-string: 0.30.19 pathe: 2.0.3 picomatch: 4.0.3 std-env: 3.9.0 @@ -9237,7 +9154,7 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.4(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1) + vite: 7.1.5(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1) vite-node: 3.2.4(@types/node@24.3.1)(jiti@2.5.1)(stylus@0.57.0)(terser@5.44.0)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: @@ -9288,10 +9205,10 @@ snapshots: chart.js: 4.5.0 vue: 3.5.21(typescript@5.9.2) - vue-eslint-parser@10.2.0(eslint@8.57.0): + vue-eslint-parser@10.2.0(eslint@9.35.0(jiti@2.5.1)): dependencies: debug: 4.4.1 - eslint: 8.57.0 + eslint: 9.35.0(jiti@2.5.1) eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 espree: 10.4.0 @@ -9434,15 +9351,15 @@ snapshots: wrap-ansi@8.1.0: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 string-width: 5.1.2 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 - wrap-ansi@9.0.0: + wrap-ansi@9.0.2: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 string-width: 7.2.0 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 wrappy@1.0.2: {} diff --git a/public/assets/img/svg/octicon-checkbox-fill.svg b/public/assets/img/svg/octicon-checkbox-fill.svg new file mode 100644 index 00000000000..6e9085f61c6 --- /dev/null +++ b/public/assets/img/svg/octicon-checkbox-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/routers/api/actions/runner/runner.go b/routers/api/actions/runner/runner.go index ce8137592d6..55ba7862a90 100644 --- a/routers/api/actions/runner/runner.go +++ b/routers/api/actions/runner/runner.go @@ -157,7 +157,7 @@ func (s *Service) FetchTask( if tasksVersion != latestVersion { // if the task version in request is not equal to the version in db, - // it means there may still be some tasks not be assgined. + // it means there may still be some tasks that haven't been assigned. // try to pick a task for the runner that send the request. if t, ok, err := actions_service.PickTask(ctx, runner); err != nil { log.Error("pick task failed: %v", err) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index bada1f6b760..66afede218c 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1247,7 +1247,7 @@ func Routes() *web.Router { }, reqToken()) m.Get("/raw/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFile) m.Get("/media/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFileOrLFS) - m.Methods("HEAD,GET", "/archive/*", reqRepoReader(unit.TypeCode), repo.GetArchive) + m.Methods("HEAD,GET", "/archive/*", reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(true), repo.GetArchive) m.Combo("/forks").Get(repo.ListForks). Post(reqToken(), reqRepoReader(unit.TypeCode), bind(api.CreateForkOption{}), repo.CreateFork) m.Post("/merge-upstream", reqToken(), mustNotBeArchived, reqRepoWriter(unit.TypeCode), bind(api.MergeUpstreamRequest{}), repo.MergeUpstream) @@ -1466,7 +1466,7 @@ func Routes() *web.Router { m.Delete("", repo.DeleteAvatar) }, reqAdmin(), reqToken()) - m.Methods("HEAD,GET", "/{ball_type:tarball|zipball|bundle}/*", reqRepoReader(unit.TypeCode), repo.DownloadArchive) + m.Methods("HEAD,GET", "/{ball_type:tarball|zipball|bundle}/*", reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(true), repo.DownloadArchive) }, repoAssignment(), checkTokenPublicOnly()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)) diff --git a/routers/api/v1/repo/download.go b/routers/api/v1/repo/download.go index acd93ecf2ec..c16605d6cd6 100644 --- a/routers/api/v1/repo/download.go +++ b/routers/api/v1/repo/download.go @@ -4,14 +4,29 @@ package repo import ( + "errors" "net/http" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/services/context" archiver_service "code.gitea.io/gitea/services/repository/archiver" ) +func serveRepoArchive(ctx *context.APIContext, reqFileName string) { + aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, reqFileName) + if err != nil { + if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) { + ctx.APIError(http.StatusBadRequest, err) + } else if errors.Is(err, archiver_service.RepoRefNotFoundError{}) { + ctx.APIError(http.StatusNotFound, err) + } else { + ctx.APIErrorInternal(err) + } + return + } + archiver_service.ServeRepoArchive(ctx.Base, ctx.Repo.Repository, ctx.Repo.GitRepo, aReq) +} + func DownloadArchive(ctx *context.APIContext) { var tp git.ArchiveType switch ballType := ctx.PathParam("ball_type"); ballType { @@ -25,27 +40,5 @@ func DownloadArchive(ctx *context.APIContext) { ctx.APIError(http.StatusBadRequest, "Unknown archive type: "+ballType) return } - - if ctx.Repo.GitRepo == nil { - var err error - ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository) - if err != nil { - ctx.APIErrorInternal(err) - return - } - } - - r, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*")+"."+tp.String()) - if err != nil { - ctx.APIErrorInternal(err) - return - } - - archive, err := r.Await(ctx) - if err != nil { - ctx.APIErrorInternal(err) - return - } - - download(ctx, r.GetArchiveName(), archive) + serveRepoArchive(ctx, ctx.PathParam("*")+"."+tp.String()) } diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index a85dda79d00..cd787b9da33 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -15,9 +15,7 @@ import ( "time" git_model "code.gitea.io/gitea/models/git" - repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/lfs" @@ -31,7 +29,6 @@ import ( "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/context" pull_service "code.gitea.io/gitea/services/pull" - archiver_service "code.gitea.io/gitea/services/repository/archiver" files_service "code.gitea.io/gitea/services/repository/files" ) @@ -282,74 +279,7 @@ func GetArchive(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - if ctx.Repo.GitRepo == nil { - var err error - ctx.Repo.GitRepo, err = gitrepo.RepositoryFromRequestContextOrOpen(ctx, ctx.Repo.Repository) - if err != nil { - ctx.APIErrorInternal(err) - return - } - } - - archiveDownload(ctx) -} - -func archiveDownload(ctx *context.APIContext) { - aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*")) - if err != nil { - if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) { - ctx.APIError(http.StatusBadRequest, err) - } else if errors.Is(err, archiver_service.RepoRefNotFoundError{}) { - ctx.APIError(http.StatusNotFound, err) - } else { - ctx.APIErrorInternal(err) - } - return - } - - archiver, err := aReq.Await(ctx) - if err != nil { - ctx.APIErrorInternal(err) - return - } - - download(ctx, aReq.GetArchiveName(), archiver) -} - -func download(ctx *context.APIContext, archiveName string, archiver *repo_model.RepoArchiver) { - downloadName := ctx.Repo.Repository.Name + "-" + archiveName - - // Add nix format link header so tarballs lock correctly: - // https://github.com/nixos/nix/blob/56763ff918eb308db23080e560ed2ea3e00c80a7/doc/manual/src/protocols/tarball-fetcher.md - ctx.Resp.Header().Add("Link", fmt.Sprintf(`<%s/archive/%s.%s?rev=%s>; rel="immutable"`, - ctx.Repo.Repository.APIURL(), - archiver.CommitID, - archiver.Type.String(), - archiver.CommitID, - )) - - rPath := archiver.RelativePath() - if setting.RepoArchive.Storage.ServeDirect() { - // If we have a signed url (S3, object storage), redirect to this directly. - u, err := storage.RepoArchives.URL(rPath, downloadName, ctx.Req.Method, nil) - if u != nil && err == nil { - ctx.Redirect(u.String()) - return - } - } - - // If we have matched and access to release or issue - fr, err := storage.RepoArchives.Open(rPath) - if err != nil { - ctx.APIErrorInternal(err) - return - } - defer fr.Close() - - ctx.ServeContent(fr, &context.ServeHeaderOptions{ - Filename: downloadName, - LastModified: archiver.CreatedUnix.AsLocalTime(), - }) + serveRepoArchive(ctx, ctx.PathParam("*")) } // GetEditorconfig get editor config of a repository diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index d4a5872fd1c..b11e889eb50 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -979,7 +979,7 @@ func DeleteIssue(ctx *context.APIContext) { return } - if err = issue_service.DeleteIssue(ctx, ctx.Doer, ctx.Repo.GitRepo, issue); err != nil { + if err = issue_service.DeleteIssue(ctx, ctx.Doer, issue); err != nil { ctx.APIErrorInternal(err) return } diff --git a/routers/init.go b/routers/init.go index 2281260a6fa..6cd85728c21 100644 --- a/routers/init.go +++ b/routers/init.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/eventsource" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" @@ -114,7 +115,7 @@ func InitWebInstallPage(ctx context.Context) { // InitWebInstalled is for the global configuration of an installed instance func InitWebInstalled(ctx context.Context) { mustInit(git.InitFull) - log.Info("Git version: %s (home: %s)", git.DefaultFeatures().VersionInfo(), git.HomeDir()) + log.Info("Git version: %s (home: %s)", git.DefaultFeatures().VersionInfo(), gitcmd.HomeDir()) if !git.DefaultFeatures().SupportHashSha256 { log.Warn("sha256 hash support is disabled - requires Git >= 2.42." + util.Iif(git.DefaultFeatures().UsingGogit, " Gogit is currently unsupported.", "")) } diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index dd9d0bc15e9..99a0b450d70 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" @@ -188,7 +189,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r // 2. Disallow force pushes to protected branches if oldCommitID != objectFormat.EmptyObjectID().String() { - output, _, err := git.NewCommand("rev-list", "--max-count=1").AddDynamicArguments(oldCommitID, "^"+newCommitID).RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath(), Env: ctx.env}) + output, _, err := gitcmd.NewCommand("rev-list", "--max-count=1").AddDynamicArguments(oldCommitID, "^"+newCommitID).RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath(), Env: ctx.env}) if err != nil { log.Error("Unable to detect force push between: %s and %s in %-v Error: %v", oldCommitID, newCommitID, repo, err) ctx.JSON(http.StatusInternalServerError, private.Response{ diff --git a/routers/private/hook_verification.go b/routers/private/hook_verification.go index 57d0964ead2..a69c3b04ebd 100644 --- a/routers/private/hook_verification.go +++ b/routers/private/hook_verification.go @@ -10,6 +10,7 @@ import ( "os" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" asymkey_service "code.gitea.io/gitea/services/asymkey" ) @@ -27,18 +28,18 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env [] _ = stdoutWriter.Close() }() - var command *git.Command + var command *gitcmd.Command objectFormat, _ := repo.GetObjectFormat() if oldCommitID == objectFormat.EmptyObjectID().String() { // When creating a new branch, the oldCommitID is empty, by using "newCommitID --not --all": // List commits that are reachable by following the newCommitID, exclude "all" existing heads/tags commits // So, it only lists the new commits received, doesn't list the commits already present in the receiving repository - command = git.NewCommand("rev-list").AddDynamicArguments(newCommitID).AddArguments("--not", "--all") + command = gitcmd.NewCommand("rev-list").AddDynamicArguments(newCommitID).AddArguments("--not", "--all") } else { - command = git.NewCommand("rev-list").AddDynamicArguments(oldCommitID + "..." + newCommitID) + command = gitcmd.NewCommand("rev-list").AddDynamicArguments(oldCommitID + "..." + newCommitID) } // This is safe as force pushes are already forbidden - err = command.Run(repo.Ctx, &git.RunOpts{ + err = command.Run(repo.Ctx, &gitcmd.RunOpts{ Env: env, Dir: repo.Path, Stdout: stdoutWriter, @@ -84,8 +85,8 @@ func readAndVerifyCommit(sha string, repo *git.Repository, env []string) error { commitID := git.MustIDFromString(sha) - return git.NewCommand("cat-file", "commit").AddDynamicArguments(sha). - Run(repo.Ctx, &git.RunOpts{ + return gitcmd.NewCommand("cat-file", "commit").AddDynamicArguments(sha). + Run(repo.Ctx, &gitcmd.RunOpts{ Env: env, Dir: repo.Path, Stdout: stdoutWriter, diff --git a/routers/private/serv.go b/routers/private/serv.go index b879be0dc20..3dfe4d21da7 100644 --- a/routers/private/serv.go +++ b/routers/private/serv.go @@ -108,6 +108,18 @@ func ServCommand(ctx *context.PrivateContext) { results.RepoName = repoName[:len(repoName)-5] } + // Check if there is a user redirect for the requested owner + redirectedUserID, err := user_model.LookupUserRedirect(ctx, results.OwnerName) + if err == nil { + owner, err := user_model.GetUserByID(ctx, redirectedUserID) + if err == nil { + log.Info("User %s has been redirected to %s", results.OwnerName, owner.Name) + results.OwnerName = owner.Name + } else { + log.Warn("User %s has a redirect to user with ID %d, but no user with this ID could be found. Trying without redirect...", results.OwnerName, redirectedUserID) + } + } + owner, err := user_model.GetUserByName(ctx, results.OwnerName) if err != nil { if user_model.IsErrUserNotExist(err) { @@ -131,6 +143,19 @@ func ServCommand(ctx *context.PrivateContext) { return } + redirectedRepoID, err := repo_model.LookupRedirect(ctx, owner.ID, results.RepoName) + if err == nil { + redirectedRepo, err := repo_model.GetRepositoryByID(ctx, redirectedRepoID) + if err == nil { + log.Info("Repository %s/%s has been redirected to %s/%s", results.OwnerName, results.RepoName, redirectedRepo.OwnerName, redirectedRepo.Name) + results.RepoName = redirectedRepo.Name + results.OwnerName = redirectedRepo.OwnerName + owner.ID = redirectedRepo.OwnerID + } else { + log.Warn("Repo %s/%s has a redirect to repo with ID %d, but no repo with this ID could be found. Trying without redirect...", results.OwnerName, results.RepoName, redirectedRepoID) + } + } + // Now get the Repository and set the results section repoExist := true repo, err := repo_model.GetRepositoryByName(ctx, owner.ID, results.RepoName) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 20f5d6262ef..2e56f6934a0 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -28,6 +28,7 @@ import ( csv_module "code.gitea.io/gitea/modules/csv" "code.gitea.io/gitea/modules/fileicon" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" @@ -524,7 +525,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { // Treat as pull request if both references are branches if ctx.Data["PageIsComparePull"] == nil { - ctx.Data["PageIsComparePull"] = headIsBranch && baseIsBranch + ctx.Data["PageIsComparePull"] = headIsBranch && baseIsBranch && permBase.CanReadIssuesOrPulls(true) } if ctx.Data["PageIsComparePull"] == true && !permBase.CanReadIssuesOrPulls(true) { @@ -569,7 +570,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { func PrepareCompareDiff( ctx *context.Context, ci *common.CompareInfo, - whitespaceBehavior git.TrustedCmdArgs, + whitespaceBehavior gitcmd.TrustedCmdArgs, ) (nothingToCompare bool) { repo := ctx.Repo.Repository headCommitID := ci.CompareInfo.HeadCommitID @@ -730,6 +731,7 @@ func CompareDiff(ctx *context.Context) { return } + ctx.Data["PageIsViewCode"] = true ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes ctx.Data["DirectComparison"] = ci.DirectComparison ctx.Data["OtherCompareSeparator"] = ".." diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index deb3ae4f3a6..ab649b6f15d 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -26,6 +26,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" @@ -315,7 +316,7 @@ func dummyInfoRefs(ctx *context.Context) { return } - refs, _, err := git.NewCommand("receive-pack", "--stateless-rpc", "--advertise-refs", ".").RunStdBytes(ctx, &git.RunOpts{Dir: tmpDir}) + refs, _, err := gitcmd.NewCommand("receive-pack", "--stateless-rpc", "--advertise-refs", ".").RunStdBytes(ctx, &gitcmd.RunOpts{Dir: tmpDir}) if err != nil { log.Error(fmt.Sprintf("%v - %s", err, string(refs))) } @@ -393,12 +394,12 @@ func (h *serviceHandler) sendFile(ctx *context.Context, contentType, file string // one or more key=value pairs separated by colons var safeGitProtocolHeader = regexp.MustCompile(`^[0-9a-zA-Z]+=[0-9a-zA-Z]+(:[0-9a-zA-Z]+=[0-9a-zA-Z]+)*$`) -func prepareGitCmdWithAllowedService(service string) (*git.Command, error) { +func prepareGitCmdWithAllowedService(service string) (*gitcmd.Command, error) { if service == "receive-pack" { - return git.NewCommand("receive-pack"), nil + return gitcmd.NewCommand("receive-pack"), nil } if service == "upload-pack" { - return git.NewCommand("upload-pack"), nil + return gitcmd.NewCommand("upload-pack"), nil } return nil, fmt.Errorf("service %q is not allowed", service) @@ -448,7 +449,7 @@ func serviceRPC(ctx *context.Context, h *serviceHandler, service string) { var stderr bytes.Buffer cmd.AddArguments("--stateless-rpc").AddDynamicArguments(h.getRepoDir()) - if err := cmd.Run(ctx, &git.RunOpts{ + if err := cmd.Run(ctx, &gitcmd.RunOpts{ Dir: h.getRepoDir(), Env: append(os.Environ(), h.environ...), Stdout: ctx.Resp, @@ -488,7 +489,7 @@ func getServiceType(ctx *context.Context) string { } func updateServerInfo(ctx gocontext.Context, dir string) []byte { - out, _, err := git.NewCommand("update-server-info").RunStdBytes(ctx, &git.RunOpts{Dir: dir}) + out, _, err := gitcmd.NewCommand("update-server-info").RunStdBytes(ctx, &gitcmd.RunOpts{Dir: dir}) if err != nil { log.Error(fmt.Sprintf("%v - %s", err, string(out))) } @@ -518,7 +519,7 @@ func GetInfoRefs(ctx *context.Context) { } h.environ = append(os.Environ(), h.environ...) - refs, _, err := cmd.AddArguments("--stateless-rpc", "--advertise-refs", ".").RunStdBytes(ctx, &git.RunOpts{Env: h.environ, Dir: h.getRepoDir()}) + refs, _, err := cmd.AddArguments("--stateless-rpc", "--advertise-refs", ".").RunStdBytes(ctx, &gitcmd.RunOpts{Env: h.environ, Dir: h.getRepoDir()}) if err != nil { log.Error(fmt.Sprintf("%v - %s", err, string(refs))) } diff --git a/routers/web/repo/issue_list.go b/routers/web/repo/issue_list.go index fd34422cfcc..a11d35da1eb 100644 --- a/routers/web/repo/issue_list.go +++ b/routers/web/repo/issue_list.go @@ -398,7 +398,7 @@ func BatchDeleteIssues(ctx *context.Context) { return } for _, issue := range issues { - if err := issue_service.DeleteIssue(ctx, ctx.Doer, ctx.Repo.GitRepo, issue); err != nil { + if err := issue_service.DeleteIssue(ctx, ctx.Doer, issue); err != nil { ctx.ServerError("DeleteIssue", err) return } diff --git a/routers/web/repo/issue_new.go b/routers/web/repo/issue_new.go index 887019b146b..1393f62fa53 100644 --- a/routers/web/repo/issue_new.go +++ b/routers/web/repo/issue_new.go @@ -216,7 +216,7 @@ func DeleteIssue(ctx *context.Context) { return } - if err := issue_service.DeleteIssue(ctx, ctx.Doer, ctx.Repo.GitRepo, issue); err != nil { + if err := issue_service.DeleteIssue(ctx, ctx.Doer, issue); err != nil { ctx.ServerError("DeleteIssueByID", err) return } diff --git a/routers/web/repo/issue_view.go b/routers/web/repo/issue_view.go index d0064e763ef..d9f6c33e3fb 100644 --- a/routers/web/repo/issue_view.go +++ b/routers/web/repo/issue_view.go @@ -12,6 +12,7 @@ import ( "strconv" activities_model "code.gitea.io/gitea/models/activities" + asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" @@ -494,7 +495,7 @@ func preparePullViewSigning(ctx *context.Context, issue *issues_model.Issue) { if ctx.Doer != nil { sign, key, _, err := asymkey_service.SignMerge(ctx, pull, ctx.Doer, pull.BaseRepo.RepoPath(), pull.BaseBranch, pull.GetGitHeadRefName()) ctx.Data["WillSign"] = sign - ctx.Data["SigningKey"] = key + ctx.Data["SigningKeyMergeDisplay"] = asymkey_model.GetDisplaySigningKey(key) if err != nil { if asymkey_service.IsErrWontSign(err) { ctx.Data["WontSignReason"] = err.(*asymkey_service.ErrWontSign).Reason diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 83aaf75363f..1ce2047b13d 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -26,7 +26,9 @@ import ( "code.gitea.io/gitea/modules/emoji" "code.gitea.io/gitea/modules/fileicon" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/graceful" issue_template "code.gitea.io/gitea/modules/issue/template" "code.gitea.io/gitea/modules/log" @@ -46,8 +48,6 @@ import ( pull_service "code.gitea.io/gitea/services/pull" repo_service "code.gitea.io/gitea/services/repository" user_service "code.gitea.io/gitea/services/user" - - "github.com/gobwas/glob" ) const ( @@ -224,7 +224,7 @@ func GetMergedBaseCommitID(ctx *context.Context, issue *issues_model.Issue) stri commitSHA, err = ctx.Repo.GitRepo.ReadPatchCommit(pull.Index) if err == nil { // Recreate pull head in files for next time - if err := ctx.Repo.GitRepo.SetReference(pull.GetGitHeadRefName(), commitSHA); err != nil { + if err := gitrepo.UpdateRef(ctx, ctx.Repo.Repository, pull.GetGitHeadRefName(), commitSHA); err != nil { log.Error("Could not write head file", err) } } else { @@ -234,7 +234,7 @@ func GetMergedBaseCommitID(ctx *context.Context, issue *issues_model.Issue) stri } if commitSHA != "" { // Get immediate parent of the first commit in the patch, grab history back - parentCommit, _, err = git.NewCommand("rev-list", "-1", "--skip=1").AddDynamicArguments(commitSHA).RunStdString(ctx, &git.RunOpts{Dir: ctx.Repo.GitRepo.Path}) + parentCommit, _, err = gitcmd.NewCommand("rev-list", "-1", "--skip=1").AddDynamicArguments(commitSHA).RunStdString(ctx, &gitcmd.RunOpts{Dir: ctx.Repo.GitRepo.Path}) if err == nil { parentCommit = strings.TrimSpace(parentCommit) } diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index 1b700aa6da0..2c137b64ab8 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -24,7 +24,6 @@ import ( "code.gitea.io/gitea/modules/optional" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/storage" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/util" @@ -376,53 +375,19 @@ func Download(ctx *context.Context) { } return } - - archiver, err := aReq.Await(ctx) - if err != nil { - ctx.ServerError("archiver.Await", err) - return - } - - download(ctx, aReq.GetArchiveName(), archiver) -} - -func download(ctx *context.Context, archiveName string, archiver *repo_model.RepoArchiver) { - downloadName := ctx.Repo.Repository.Name + "-" + archiveName - - // Add nix format link header so tarballs lock correctly: - // https://github.com/nixos/nix/blob/56763ff918eb308db23080e560ed2ea3e00c80a7/doc/manual/src/protocols/tarball-fetcher.md - ctx.Resp.Header().Add("Link", fmt.Sprintf(`<%s/archive/%s.tar.gz?rev=%s>; rel="immutable"`, - ctx.Repo.Repository.APIURL(), - archiver.CommitID, archiver.CommitID)) - - rPath := archiver.RelativePath() - if setting.RepoArchive.Storage.ServeDirect() { - // If we have a signed url (S3, object storage), redirect to this directly. - u, err := storage.RepoArchives.URL(rPath, downloadName, ctx.Req.Method, nil) - if u != nil && err == nil { - ctx.Redirect(u.String()) - return - } - } - - // If we have matched and access to release or issue - fr, err := storage.RepoArchives.Open(rPath) - if err != nil { - ctx.ServerError("Open", err) - return - } - defer fr.Close() - - ctx.ServeContent(fr, &context.ServeHeaderOptions{ - Filename: downloadName, - LastModified: archiver.CreatedUnix.AsLocalTime(), - }) + archiver_service.ServeRepoArchive(ctx.Base, ctx.Repo.Repository, ctx.Repo.GitRepo, aReq) } // InitiateDownload will enqueue an archival request, as needed. It may submit // a request that's already in-progress, but the archiver service will just // kind of drop it on the floor if this is the case. func InitiateDownload(ctx *context.Context) { + if setting.Repository.StreamArchives { + ctx.JSON(http.StatusOK, map[string]any{ + "complete": true, + }) + return + } aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*")) if err != nil { ctx.HTTPError(http.StatusBadRequest, "invalid archive request") diff --git a/routers/web/repo/setting/protected_branch.go b/routers/web/repo/setting/protected_branch.go index 0eea5e3f340..152f67550cc 100644 --- a/routers/web/repo/setting/protected_branch.go +++ b/routers/web/repo/setting/protected_branch.go @@ -19,6 +19,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/web/repo" @@ -26,8 +27,6 @@ import ( "code.gitea.io/gitea/services/forms" pull_service "code.gitea.io/gitea/services/pull" "code.gitea.io/gitea/services/repository" - - "github.com/gobwas/glob" ) const ( diff --git a/routers/web/user/package.go b/routers/web/user/package.go index d130d1dca18..924a10041b0 100644 --- a/routers/web/user/package.go +++ b/routers/web/user/package.go @@ -431,74 +431,81 @@ func PackageSettings(ctx *context.Context) { ctx.Data["Title"] = pd.Package.Name ctx.Data["IsPackagesPage"] = true ctx.Data["PackageDescriptor"] = pd - - repos, _, _ := repo_model.GetUserRepositories(ctx, repo_model.SearchRepoOptions{ - Actor: pd.Owner, - Private: true, - }) - ctx.Data["Repos"] = repos ctx.Data["CanWritePackages"] = ctx.Package.AccessMode >= perm.AccessModeWrite || ctx.IsUserSiteAdmin() + if pd.Package.RepoID > 0 { + repo, err := repo_model.GetRepositoryByID(ctx, pd.Package.RepoID) + if err != nil { + ctx.ServerError("GetRepositoryByID", err) + return + } + ctx.Data["LinkedRepoName"] = repo.Name + } + ctx.HTML(http.StatusOK, tplPackagesSettings) } // PackageSettingsPost updates the package settings func PackageSettingsPost(ctx *context.Context) { - pd := ctx.Package.Descriptor - form := web.GetForm(ctx).(*forms.PackageSettingForm) switch form.Action { case "link": - success := func() bool { - repoID := int64(0) - if form.RepoID != 0 { - repo, err := repo_model.GetRepositoryByID(ctx, form.RepoID) - if err != nil { - log.Error("Error getting repository: %v", err) - return false - } - - if repo.OwnerID != pd.Owner.ID { - return false - } - - repoID = repo.ID - } - - if err := packages_model.SetRepositoryLink(ctx, pd.Package.ID, repoID); err != nil { - log.Error("Error updating package: %v", err) - return false - } - - return true - }() - - if success { - ctx.Flash.Success(ctx.Tr("packages.settings.link.success")) - } else { - ctx.Flash.Error(ctx.Tr("packages.settings.link.error")) - } - - ctx.Redirect(ctx.Link) - return + packageSettingsPostActionLink(ctx, form) case "delete": - err := packages_service.RemovePackageVersion(ctx, ctx.Doer, ctx.Package.Descriptor.Version) - if err != nil { - log.Error("Error deleting package: %v", err) - ctx.Flash.Error(ctx.Tr("packages.settings.delete.error")) - } else { - ctx.Flash.Success(ctx.Tr("packages.settings.delete.success")) + packageSettingsPostActionDelete(ctx) + default: + ctx.NotFound(nil) + } +} + +func packageSettingsPostActionLink(ctx *context.Context, form *forms.PackageSettingForm) { + pd := ctx.Package.Descriptor + if form.RepoName == "" { // remove the link + if err := packages_model.SetRepositoryLink(ctx, pd.Package.ID, 0); err != nil { + ctx.JSONError(ctx.Tr("packages.settings.unlink.error")) + return } - redirectURL := ctx.Package.Owner.HomeLink() + "/-/packages" - // redirect to the package if there are still versions available - if has, _ := packages_model.ExistVersion(ctx, &packages_model.PackageSearchOptions{PackageID: ctx.Package.Descriptor.Package.ID, IsInternal: optional.Some(false)}); has { - redirectURL = ctx.Package.Descriptor.PackageWebLink() - } - - ctx.Redirect(redirectURL) + ctx.Flash.Success(ctx.Tr("packages.settings.unlink.success")) + ctx.JSONRedirect("") return } + + repo, err := repo_model.GetRepositoryByName(ctx, pd.Owner.ID, form.RepoName) + if err != nil { + if repo_model.IsErrRepoNotExist(err) { + ctx.JSONError(ctx.Tr("packages.settings.link.repo_not_found", form.RepoName)) + } else { + ctx.ServerError("GetRepositoryByOwnerAndName", err) + } + return + } + + if err := packages_model.SetRepositoryLink(ctx, pd.Package.ID, repo.ID); err != nil { + ctx.JSONError(ctx.Tr("packages.settings.link.error")) + return + } + + ctx.Flash.Success(ctx.Tr("packages.settings.link.success")) + ctx.JSONRedirect("") +} + +func packageSettingsPostActionDelete(ctx *context.Context) { + err := packages_service.RemovePackageVersion(ctx, ctx.Doer, ctx.Package.Descriptor.Version) + if err != nil { + log.Error("Error deleting package: %v", err) + ctx.Flash.Error(ctx.Tr("packages.settings.delete.error")) + } else { + ctx.Flash.Success(ctx.Tr("packages.settings.delete.success")) + } + + redirectURL := ctx.Package.Owner.HomeLink() + "/-/packages" + // redirect to the package if there are still versions available + if has, _ := packages_model.ExistVersion(ctx, &packages_model.PackageSearchOptions{PackageID: ctx.Package.Descriptor.Package.ID, IsInternal: optional.Some(false)}); has { + redirectURL = ctx.Package.Descriptor.PackageWebLink() + } + + ctx.Redirect(redirectURL) } // DownloadPackageFile serves the content of a package file diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index b8bc20cdbb1..66916e93017 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -281,7 +281,7 @@ func skipWorkflows(ctx context.Context, input *notifyInput, commit *git.Commit) return true } } - // skip workflow runs events exceeding the maxiumum of 5 recursive events + // skip workflow runs events exceeding the maximum of 5 recursive events log.Debug("repo %s: skipped workflow_run because of recursive event of 5", input.Repo.RepoPath()) return true } diff --git a/services/agit/agit.go b/services/agit/agit.go index 9d2d122cfb3..4ed867b3583 100644 --- a/services/agit/agit.go +++ b/services/agit/agit.go @@ -15,6 +15,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" @@ -198,9 +199,9 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. } if !forcePush.Value() { - output, _, err := git.NewCommand("rev-list", "--max-count=1"). + output, _, err := gitcmd.NewCommand("rev-list", "--max-count=1"). AddDynamicArguments(oldCommitID, "^"+opts.NewCommitIDs[i]). - RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath(), Env: os.Environ()}) + RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath(), Env: os.Environ()}) if err != nil { return nil, fmt.Errorf("failed to detect force push: %w", err) } else if len(output) > 0 { diff --git a/services/asymkey/sign.go b/services/asymkey/sign.go index 8fd2adc8b48..4fde342bea5 100644 --- a/services/asymkey/sign.go +++ b/services/asymkey/sign.go @@ -17,6 +17,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" @@ -116,16 +117,16 @@ func SigningKey(ctx context.Context, repoPath string) (*git.SigningKey, *git.Sig if setting.Repository.Signing.SigningKey == "default" || setting.Repository.Signing.SigningKey == "" { // Can ignore the error here as it means that commit.gpgsign is not set - value, _, _ := git.NewCommand("config", "--get", "commit.gpgsign").RunStdString(ctx, &git.RunOpts{Dir: repoPath}) + value, _, _ := gitcmd.NewCommand("config", "--get", "commit.gpgsign").RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) sign, valid := git.ParseBool(strings.TrimSpace(value)) if !sign || !valid { return nil, nil } - format, _, _ := git.NewCommand("config", "--default", git.SigningKeyFormatOpenPGP, "--get", "gpg.format").RunStdString(ctx, &git.RunOpts{Dir: repoPath}) - signingKey, _, _ := git.NewCommand("config", "--get", "user.signingkey").RunStdString(ctx, &git.RunOpts{Dir: repoPath}) - signingName, _, _ := git.NewCommand("config", "--get", "user.name").RunStdString(ctx, &git.RunOpts{Dir: repoPath}) - signingEmail, _, _ := git.NewCommand("config", "--get", "user.email").RunStdString(ctx, &git.RunOpts{Dir: repoPath}) + format, _, _ := gitcmd.NewCommand("config", "--default", git.SigningKeyFormatOpenPGP, "--get", "gpg.format").RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) + signingKey, _, _ := gitcmd.NewCommand("config", "--get", "user.signingkey").RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) + signingName, _, _ := gitcmd.NewCommand("config", "--get", "user.name").RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) + signingEmail, _, _ := gitcmd.NewCommand("config", "--get", "user.email").RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if strings.TrimSpace(signingKey) == "" { return nil, nil diff --git a/services/context/csrf.go b/services/context/csrf.go index 7b475a8fd85..f190465bdb3 100644 --- a/services/context/csrf.go +++ b/services/context/csrf.go @@ -48,7 +48,7 @@ type csrfProtector struct { opt CsrfOptions // id must be unique per user. id string - // token is the valid one which wil be used by end user and passed via header, cookie, or hidden form value. + // token is the valid one which will be used by end user and passed via header, cookie, or hidden form value. token string } diff --git a/services/context/repo.go b/services/context/repo.go index afc6de9b166..04ddba70fe0 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -14,6 +14,7 @@ import ( "path" "strings" + asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" @@ -99,7 +100,7 @@ type CommitFormOptions struct { UserCanPush bool RequireSigned bool WillSign bool - SigningKey *git.SigningKey + SigningKeyFormDisplay string WontSignReason string CanCreatePullRequest bool CanCreateBasePullRequest bool @@ -139,7 +140,7 @@ func PrepareCommitFormOptions(ctx *Context, doer *user_model.User, targetRepo *r protectionRequireSigned = protectedBranch.RequireSignedCommits } - willSign, signKeyID, _, err := asymkey_service.SignCRUDAction(ctx, targetRepo.RepoPath(), doer, targetRepo.RepoPath(), refName.String()) + willSign, signKey, _, err := asymkey_service.SignCRUDAction(ctx, targetRepo.RepoPath(), doer, targetRepo.RepoPath(), refName.String()) wontSignReason := "" if asymkey_service.IsErrWontSign(err) { wontSignReason = string(err.(*asymkey_service.ErrWontSign).Reason) @@ -156,14 +157,14 @@ func PrepareCommitFormOptions(ctx *Context, doer *user_model.User, targetRepo *r canCreatePullRequest := targetRepo.UnitEnabled(ctx, unit_model.TypePullRequests) || canCreateBasePullRequest opts := &CommitFormOptions{ - TargetRepo: targetRepo, - WillSubmitToFork: submitToForkedRepo, - CanCommitToBranch: canCommitToBranch, - UserCanPush: canPushWithProtection, - RequireSigned: protectionRequireSigned, - WillSign: willSign, - SigningKey: signKeyID, - WontSignReason: wontSignReason, + TargetRepo: targetRepo, + WillSubmitToFork: submitToForkedRepo, + CanCommitToBranch: canCommitToBranch, + UserCanPush: canPushWithProtection, + RequireSigned: protectionRequireSigned, + WillSign: willSign, + SigningKeyFormDisplay: asymkey_model.GetDisplaySigningKey(signKey), + WontSignReason: wontSignReason, CanCreatePullRequest: canCreatePullRequest, CanCreateBasePullRequest: canCreateBasePullRequest, diff --git a/services/convert/issue.go b/services/convert/issue.go index ecb3b93af1a..e26412bcca7 100644 --- a/services/convert/issue.go +++ b/services/convert/issue.go @@ -59,6 +59,8 @@ func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Iss Created: issue.CreatedUnix.AsTime(), Updated: issue.UpdatedUnix.AsTime(), PinOrder: util.Iif(issue.PinOrder == -1, 0, issue.PinOrder), // -1 means loaded with no pin order + + TimeEstimate: issue.TimeEstimate, } if issue.Repo != nil { diff --git a/services/cron/tasks_basic.go b/services/cron/tasks_basic.go index 841981787df..48380b7b9ad 100644 --- a/services/cron/tasks_basic.go +++ b/services/cron/tasks_basic.go @@ -11,7 +11,7 @@ import ( git_model "code.gitea.io/gitea/models/git" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/models/webhook" - "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/migrations" @@ -59,7 +59,7 @@ func registerRepoHealthCheck() { }, func(ctx context.Context, _ *user_model.User, config Config) error { rhcConfig := config.(*RepoHealthCheckConfig) // the git args are set by config, they can be safe to be trusted - return repo_service.GitFsckRepos(ctx, rhcConfig.Timeout, git.ToTrustedCmdArgs(rhcConfig.Args)) + return repo_service.GitFsckRepos(ctx, rhcConfig.Timeout, gitcmd.ToTrustedCmdArgs(rhcConfig.Args)) }) } diff --git a/services/cron/tasks_extended.go b/services/cron/tasks_extended.go index 37471119842..74fb12430d1 100644 --- a/services/cron/tasks_extended.go +++ b/services/cron/tasks_extended.go @@ -10,7 +10,7 @@ import ( activities_model "code.gitea.io/gitea/models/activities" "code.gitea.io/gitea/models/system" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" issue_indexer "code.gitea.io/gitea/modules/indexer/issues" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/updatechecker" @@ -61,7 +61,7 @@ func registerGarbageCollectRepositories() { }, func(ctx context.Context, _ *user_model.User, config Config) error { rhcConfig := config.(*RepoHealthCheckConfig) // the git args are set by config, they can be safe to be trusted - return repo_service.GitGcRepos(ctx, rhcConfig.Timeout, git.ToTrustedCmdArgs(rhcConfig.Args)) + return repo_service.GitGcRepos(ctx, rhcConfig.Timeout, gitcmd.ToTrustedCmdArgs(rhcConfig.Args)) }) } diff --git a/services/doctor/heads.go b/services/doctor/heads.go index bbfd40da5ec..43545058069 100644 --- a/services/doctor/heads.go +++ b/services/doctor/heads.go @@ -8,6 +8,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" ) @@ -18,9 +19,9 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) numReposUpdated := 0 err := iterateRepositories(ctx, func(repo *repo_model.Repository) error { numRepos++ - _, _, defaultBranchErr := git.NewCommand("rev-parse").AddDashesAndList(repo.DefaultBranch).RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}) + _, _, defaultBranchErr := gitcmd.NewCommand("rev-parse").AddDashesAndList(repo.DefaultBranch).RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) - head, _, headErr := git.NewCommand("symbolic-ref", "--short", "HEAD").RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}) + head, _, headErr := gitcmd.NewCommand("symbolic-ref", "--short", "HEAD").RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) // what we expect: default branch is valid, and HEAD points to it if headErr == nil && defaultBranchErr == nil && head == repo.DefaultBranch { @@ -46,7 +47,7 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) } // otherwise, let's try fixing HEAD - err := git.NewCommand("symbolic-ref").AddDashesAndList("HEAD", git.BranchPrefix+repo.DefaultBranch).Run(ctx, &git.RunOpts{Dir: repo.RepoPath()}) + err := gitcmd.NewCommand("symbolic-ref").AddDashesAndList("HEAD", git.BranchPrefix+repo.DefaultBranch).Run(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) if err != nil { logger.Warn("Failed to fix HEAD for %s/%s: %v", repo.OwnerName, repo.Name, err) return nil diff --git a/services/doctor/mergebase.go b/services/doctor/mergebase.go index cbd8aa59fdb..a078d54dad6 100644 --- a/services/doctor/mergebase.go +++ b/services/doctor/mergebase.go @@ -12,6 +12,7 @@ import ( issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" "xorm.io/builder" @@ -42,17 +43,17 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro if !pr.HasMerged { var err error - pr.MergeBase, _, err = git.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, pr.GetGitHeadRefName()).RunStdString(ctx, &git.RunOpts{Dir: repoPath}) + pr.MergeBase, _, err = gitcmd.NewCommand("merge-base").AddDashesAndList(pr.BaseBranch, pr.GetGitHeadRefName()).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if err != nil { var err2 error - pr.MergeBase, _, err2 = git.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix+pr.BaseBranch).RunStdString(ctx, &git.RunOpts{Dir: repoPath}) + pr.MergeBase, _, err2 = gitcmd.NewCommand("rev-parse").AddDynamicArguments(git.BranchPrefix+pr.BaseBranch).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if err2 != nil { logger.Warn("Unable to get merge base for PR ID %d, #%d onto %s in %s/%s. Error: %v & %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err, err2) return nil } } } else { - parentsString, _, err := git.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(ctx, &git.RunOpts{Dir: repoPath}) + parentsString, _, err := gitcmd.NewCommand("rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if err != nil { logger.Warn("Unable to get parents for merged PR ID %d, #%d onto %s in %s/%s. Error: %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err) return nil @@ -64,8 +65,8 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro refs := append([]string{}, parents[1:]...) refs = append(refs, pr.GetGitHeadRefName()) - cmd := git.NewCommand("merge-base").AddDashesAndList(refs...) - pr.MergeBase, _, err = cmd.RunStdString(ctx, &git.RunOpts{Dir: repoPath}) + cmd := gitcmd.NewCommand("merge-base").AddDashesAndList(refs...) + pr.MergeBase, _, err = cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}) if err != nil { logger.Warn("Unable to get merge base for merged PR ID %d, #%d onto %s in %s/%s. Error: %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err) return nil diff --git a/services/forms/user_form.go b/services/forms/user_form.go index ddf2bd09b0b..618294d4341 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -416,8 +416,8 @@ func (f *WebauthnDeleteForm) Validate(req *http.Request, errs binding.Errors) bi // PackageSettingForm form for package settings type PackageSettingForm struct { - Action string - RepoID int64 `form:"repo_id"` + Action string + RepoName string `form:"repo_name"` } // Validate validates the fields diff --git a/services/forms/user_form_test.go b/services/forms/user_form_test.go index 09e9ec0f65c..4246f955b3e 100644 --- a/services/forms/user_form_test.go +++ b/services/forms/user_form_test.go @@ -6,10 +6,10 @@ package forms import ( "testing" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/test" - "github.com/gobwas/glob" "github.com/stretchr/testify/assert" ) diff --git a/services/gitdiff/git_diff_tree.go b/services/gitdiff/git_diff_tree.go index ed94bfbfe45..eeb354c1b6e 100644 --- a/services/gitdiff/git_diff_tree.go +++ b/services/gitdiff/git_diff_tree.go @@ -13,6 +13,7 @@ import ( "strings" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" ) @@ -55,12 +56,12 @@ func runGitDiffTree(ctx context.Context, gitRepo *git.Repository, useMergeBase b return nil, err } - cmd := git.NewCommand("diff-tree", "--raw", "-r", "--find-renames", "--root") + cmd := gitcmd.NewCommand("diff-tree", "--raw", "-r", "--find-renames", "--root") if useMergeBase { cmd.AddArguments("--merge-base") } cmd.AddDynamicArguments(baseCommitID, headCommitID) - stdout, _, runErr := cmd.RunStdString(ctx, &git.RunOpts{Dir: gitRepo.Path}) + stdout, _, runErr := cmd.RunStdString(ctx, &gitcmd.RunOpts{Dir: gitRepo.Path}) if runErr != nil { log.Warn("git diff-tree: %v", runErr) return nil, runErr diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 7c99e049d54..1bb7c05ba2c 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -26,6 +26,7 @@ import ( "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git/attribute" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" @@ -1090,7 +1091,7 @@ type DiffOptions struct { MaxLines int MaxLineCharacters int MaxFiles int - WhitespaceBehavior git.TrustedCmdArgs + WhitespaceBehavior gitcmd.TrustedCmdArgs DirectComparison bool } @@ -1131,7 +1132,7 @@ func getDiffBasic(ctx context.Context, gitRepo *git.Repository, opts *DiffOption return nil, nil, nil, err } - cmdDiff := git.NewCommand(). + cmdDiff := gitcmd.NewCommand(). AddArguments("diff", "--src-prefix=\\a/", "--dst-prefix=\\b/", "-M"). AddArguments(opts.WhitespaceBehavior...) @@ -1158,7 +1159,7 @@ func getDiffBasic(ctx context.Context, gitRepo *git.Repository, opts *DiffOption go func() { stderr := &bytes.Buffer{} - if err := cmdDiff.Run(cmdCtx, &git.RunOpts{ + if err := cmdDiff.Run(cmdCtx, &gitcmd.RunOpts{ Timeout: time.Duration(setting.Git.Timeout.Default) * time.Second, Dir: repoPath, Stdout: writer, @@ -1395,8 +1396,8 @@ func CommentMustAsDiff(ctx context.Context, c *issues_model.Comment) *Diff { } // GetWhitespaceFlag returns git diff flag for treating whitespaces -func GetWhitespaceFlag(whitespaceBehavior string) git.TrustedCmdArgs { - whitespaceFlags := map[string]git.TrustedCmdArgs{ +func GetWhitespaceFlag(whitespaceBehavior string) gitcmd.TrustedCmdArgs { + whitespaceFlags := map[string]gitcmd.TrustedCmdArgs{ "ignore-all": {"-w"}, "ignore-change": {"-b"}, "ignore-eol": {"--ignore-space-at-eol"}, diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go index 83815caeb56..7b64b6b5f8a 100644 --- a/services/gitdiff/gitdiff_test.go +++ b/services/gitdiff/gitdiff_test.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/setting" @@ -602,7 +603,7 @@ func TestGetDiffRangeWithWhitespaceBehavior(t *testing.T) { require.NoError(t, err) defer gitRepo.Close() - for _, behavior := range []git.TrustedCmdArgs{{"-w"}, {"--ignore-space-at-eol"}, {"-b"}, nil} { + for _, behavior := range []gitcmd.TrustedCmdArgs{{"-w"}, {"--ignore-space-at-eol"}, {"-b"}, nil} { diffs, err := GetDiffForAPI(t.Context(), gitRepo, &DiffOptions{ AfterCommitID: "d8e0bbb45f200e67d9a784ce55bd90821af45ebd", diff --git a/services/issue/issue.go b/services/issue/issue.go index b9ee66c1f27..611fc6806b1 100644 --- a/services/issue/issue.go +++ b/services/issue/issue.go @@ -17,6 +17,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/storage" notify_service "code.gitea.io/gitea/services/notify" ) @@ -165,7 +166,7 @@ func UpdateAssignees(ctx context.Context, issue *issues_model.Issue, oneAssignee } // DeleteIssue deletes an issue -func DeleteIssue(ctx context.Context, doer *user_model.User, gitRepo *git.Repository, issue *issues_model.Issue) error { +func DeleteIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) error { // load issue before deleting it if err := issue.LoadAttributes(ctx); err != nil { return err @@ -184,8 +185,11 @@ func DeleteIssue(ctx context.Context, doer *user_model.User, gitRepo *git.Reposi } // delete pull request related git data - if issue.IsPull && gitRepo != nil { - if err := gitRepo.RemoveReference(issue.PullRequest.GetGitHeadRefName()); err != nil { + if issue.IsPull { + if err := issue.PullRequest.LoadBaseRepo(ctx); err != nil { + return err + } + if err := gitrepo.RemoveRef(ctx, issue.PullRequest.BaseRepo, issue.PullRequest.GetGitHeadRefName()); err != nil { return err } } @@ -246,74 +250,67 @@ func GetRefEndNamesAndURLs(issues []*issues_model.Issue, repoLink string) (map[i // deleteIssue deletes the issue func deleteIssue(ctx context.Context, issue *issues_model.Issue) ([]string, error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, err - } - defer committer.Close() - - if _, err := db.GetEngine(ctx).ID(issue.ID).NoAutoCondition().Delete(issue); err != nil { - return nil, err - } - - // update the total issue numbers - if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, false); err != nil { - return nil, err - } - // if the issue is closed, update the closed issue numbers - if issue.IsClosed { - if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, true); err != nil { + return db.WithTx2(ctx, func(ctx context.Context) ([]string, error) { + if _, err := db.GetEngine(ctx).ID(issue.ID).NoAutoCondition().Delete(issue); err != nil { return nil, err } - } - if err := issues_model.UpdateMilestoneCounters(ctx, issue.MilestoneID); err != nil { - return nil, fmt.Errorf("error updating counters for milestone id %d: %w", - issue.MilestoneID, err) - } + // update the total issue numbers + if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, false); err != nil { + return nil, err + } + // if the issue is closed, update the closed issue numbers + if issue.IsClosed { + if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, true); err != nil { + return nil, err + } + } - if err := activities_model.DeleteIssueActions(ctx, issue.RepoID, issue.ID, issue.Index); err != nil { - return nil, err - } + if err := issues_model.UpdateMilestoneCounters(ctx, issue.MilestoneID); err != nil { + return nil, fmt.Errorf("error updating counters for milestone id %d: %w", + issue.MilestoneID, err) + } - // find attachments related to this issue and remove them - if err := issue.LoadAttachments(ctx); err != nil { - return nil, err - } + if err := activities_model.DeleteIssueActions(ctx, issue.RepoID, issue.ID, issue.Index); err != nil { + return nil, err + } - var attachmentPaths []string - for i := range issue.Attachments { - attachmentPaths = append(attachmentPaths, issue.Attachments[i].RelativePath()) - } + // find attachments related to this issue and remove them + if err := issue.LoadAttachments(ctx); err != nil { + return nil, err + } - // delete all database data still assigned to this issue - if err := db.DeleteBeans(ctx, - &issues_model.ContentHistory{IssueID: issue.ID}, - &issues_model.Comment{IssueID: issue.ID}, - &issues_model.IssueLabel{IssueID: issue.ID}, - &issues_model.IssueDependency{IssueID: issue.ID}, - &issues_model.IssueAssignees{IssueID: issue.ID}, - &issues_model.IssueUser{IssueID: issue.ID}, - &activities_model.Notification{IssueID: issue.ID}, - &issues_model.Reaction{IssueID: issue.ID}, - &issues_model.IssueWatch{IssueID: issue.ID}, - &issues_model.Stopwatch{IssueID: issue.ID}, - &issues_model.TrackedTime{IssueID: issue.ID}, - &project_model.ProjectIssue{IssueID: issue.ID}, - &repo_model.Attachment{IssueID: issue.ID}, - &issues_model.PullRequest{IssueID: issue.ID}, - &issues_model.Comment{RefIssueID: issue.ID}, - &issues_model.IssueDependency{DependencyID: issue.ID}, - &issues_model.Comment{DependentIssueID: issue.ID}, - &issues_model.IssuePin{IssueID: issue.ID}, - ); err != nil { - return nil, err - } + var attachmentPaths []string + for i := range issue.Attachments { + attachmentPaths = append(attachmentPaths, issue.Attachments[i].RelativePath()) + } - if err := committer.Commit(); err != nil { - return nil, err - } - return attachmentPaths, nil + // delete all database data still assigned to this issue + if err := db.DeleteBeans(ctx, + &issues_model.ContentHistory{IssueID: issue.ID}, + &issues_model.Comment{IssueID: issue.ID}, + &issues_model.IssueLabel{IssueID: issue.ID}, + &issues_model.IssueDependency{IssueID: issue.ID}, + &issues_model.IssueAssignees{IssueID: issue.ID}, + &issues_model.IssueUser{IssueID: issue.ID}, + &activities_model.Notification{IssueID: issue.ID}, + &issues_model.Reaction{IssueID: issue.ID}, + &issues_model.IssueWatch{IssueID: issue.ID}, + &issues_model.Stopwatch{IssueID: issue.ID}, + &issues_model.TrackedTime{IssueID: issue.ID}, + &project_model.ProjectIssue{IssueID: issue.ID}, + &repo_model.Attachment{IssueID: issue.ID}, + &issues_model.PullRequest{IssueID: issue.ID}, + &issues_model.Comment{RefIssueID: issue.ID}, + &issues_model.IssueDependency{DependencyID: issue.ID}, + &issues_model.Comment{DependentIssueID: issue.ID}, + &issues_model.IssuePin{IssueID: issue.ID}, + ); err != nil { + return nil, err + } + + return attachmentPaths, nil + }) } // DeleteOrphanedIssues delete issues without a repo diff --git a/services/migrations/dump.go b/services/migrations/dump.go index 8edd567b082..385759800e4 100644 --- a/services/migrations/dump.go +++ b/services/migrations/dump.go @@ -18,6 +18,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" base "code.gitea.io/gitea/modules/migration" "code.gitea.io/gitea/modules/repository" @@ -488,7 +489,7 @@ func (g *RepositoryDumper) handlePullRequest(ctx context.Context, pr *base.PullR if pr.Head.CloneURL == "" || pr.Head.Ref == "" { // Set head information if pr.Head.SHA is available if pr.Head.SHA != "" { - _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.gitPath()}) + _, _, err = gitcmd.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).RunStdString(ctx, &gitcmd.RunOpts{Dir: g.gitPath()}) if err != nil { log.Error("PR #%d in %s/%s unable to update-ref for pr HEAD: %v", pr.Number, g.repoOwner, g.repoName, err) } @@ -518,7 +519,7 @@ func (g *RepositoryDumper) handlePullRequest(ctx context.Context, pr *base.PullR if !ok { // Set head information if pr.Head.SHA is available if pr.Head.SHA != "" { - _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.gitPath()}) + _, _, err = gitcmd.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).RunStdString(ctx, &gitcmd.RunOpts{Dir: g.gitPath()}) if err != nil { log.Error("PR #%d in %s/%s unable to update-ref for pr HEAD: %v", pr.Number, g.repoOwner, g.repoName, err) } @@ -553,7 +554,7 @@ func (g *RepositoryDumper) handlePullRequest(ctx context.Context, pr *base.PullR fetchArg = git.BranchPrefix + fetchArg } - _, _, err = git.NewCommand("fetch", "--no-tags").AddDashesAndList(remote, fetchArg).RunStdString(ctx, &git.RunOpts{Dir: g.gitPath()}) + _, _, err = gitcmd.NewCommand("fetch", "--no-tags").AddDashesAndList(remote, fetchArg).RunStdString(ctx, &gitcmd.RunOpts{Dir: g.gitPath()}) if err != nil { log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err) // We need to continue here so that the Head.Ref is reset and we attempt to set the gitref for the PR @@ -577,7 +578,7 @@ func (g *RepositoryDumper) handlePullRequest(ctx context.Context, pr *base.PullR pr.Head.SHA = headSha } if pr.Head.SHA != "" { - _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.gitPath()}) + _, _, err = gitcmd.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).RunStdString(ctx, &gitcmd.RunOpts{Dir: g.gitPath()}) if err != nil { log.Error("unable to set %s as the local head for PR #%d from %s in %s/%s. Error: %v", pr.Head.SHA, pr.Number, pr.Head.Ref, g.repoOwner, g.repoName, err) } diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 75eb06d01fa..02c61bd0f16 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -20,6 +20,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/label" "code.gitea.io/gitea/modules/log" @@ -662,7 +663,7 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(ctx context.Context, pr *ba fetchArg = git.BranchPrefix + fetchArg } - _, _, err = git.NewCommand("fetch", "--no-tags").AddDashesAndList(remote, fetchArg).RunStdString(ctx, &git.RunOpts{Dir: g.repo.RepoPath()}) + _, _, err = gitcmd.NewCommand("fetch", "--no-tags").AddDashesAndList(remote, fetchArg).RunStdString(ctx, &gitcmd.RunOpts{Dir: g.repo.RepoPath()}) if err != nil { log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err) return head, nil @@ -681,8 +682,7 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(ctx context.Context, pr *ba pr.Head.SHA = headSha } - _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.repo.RepoPath()}) - if err != nil { + if err = gitrepo.UpdateRef(ctx, g.repo, pr.GetGitHeadRefName(), pr.Head.SHA); err != nil { return "", err } @@ -698,14 +698,13 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(ctx context.Context, pr *ba // The SHA is empty log.Warn("Empty reference, no pull head for PR #%d in %s/%s", pr.Number, g.repoOwner, g.repoName) } else { - _, _, err = git.NewCommand("rev-list", "--quiet", "-1").AddDynamicArguments(pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.repo.RepoPath()}) + _, _, err = gitcmd.NewCommand("rev-list", "--quiet", "-1").AddDynamicArguments(pr.Head.SHA).RunStdString(ctx, &gitcmd.RunOpts{Dir: g.repo.RepoPath()}) if err != nil { // Git update-ref remove bad references with a relative path log.Warn("Deprecated local head %s for PR #%d in %s/%s, removing %s", pr.Head.SHA, pr.Number, g.repoOwner, g.repoName, pr.GetGitHeadRefName()) } else { // set head information - _, _, err = git.NewCommand("update-ref", "--no-deref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.Head.SHA).RunStdString(ctx, &git.RunOpts{Dir: g.repo.RepoPath()}) - if err != nil { + if err = gitrepo.UpdateRef(ctx, g.repo, pr.GetGitHeadRefName(), pr.Head.SHA); err != nil { log.Error("unable to set %s as the local head for PR #%d from %s in %s/%s. Error: %v", pr.Head.SHA, pr.Number, pr.Head.Ref, g.repoOwner, g.repoName, err) } } diff --git a/services/migrations/gitea_uploader_test.go b/services/migrations/gitea_uploader_test.go index 4856b66c6e1..08efef5de6d 100644 --- a/services/migrations/gitea_uploader_test.go +++ b/services/migrations/gitea_uploader_test.go @@ -18,6 +18,7 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" @@ -238,7 +239,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) { baseRef := "master" // this is very different from the real situation. It should be a bare repository for all the Gitea managed repositories assert.NoError(t, git.InitRepository(t.Context(), fromRepo.RepoPath(), false, fromRepo.ObjectFormatName)) - err := git.NewCommand("symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseRef).Run(t.Context(), &git.RunOpts{Dir: fromRepo.RepoPath()}) + err := gitcmd.NewCommand("symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseRef).Run(t.Context(), &gitcmd.RunOpts{Dir: fromRepo.RepoPath()}) assert.NoError(t, err) assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte("# Testing Repository\n\nOriginally created in: "+fromRepo.RepoPath()), 0o644)) assert.NoError(t, git.AddChanges(t.Context(), fromRepo.RepoPath(), true)) @@ -262,7 +263,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) { // fromRepo branch1 // headRef := "branch1" - _, _, err = git.NewCommand("checkout", "-b").AddDynamicArguments(headRef).RunStdString(t.Context(), &git.RunOpts{Dir: fromRepo.RepoPath()}) + _, _, err = gitcmd.NewCommand("checkout", "-b").AddDynamicArguments(headRef).RunStdString(t.Context(), &gitcmd.RunOpts{Dir: fromRepo.RepoPath()}) assert.NoError(t, err) assert.NoError(t, os.WriteFile(filepath.Join(fromRepo.RepoPath(), "README.md"), []byte("SOMETHING"), 0o644)) assert.NoError(t, git.AddChanges(t.Context(), fromRepo.RepoPath(), true)) @@ -286,7 +287,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) { assert.NoError(t, git.Clone(t.Context(), fromRepo.RepoPath(), forkRepo.RepoPath(), git.CloneRepoOptions{ Branch: headRef, })) - _, _, err = git.NewCommand("checkout", "-b").AddDynamicArguments(forkHeadRef).RunStdString(t.Context(), &git.RunOpts{Dir: forkRepo.RepoPath()}) + _, _, err = gitcmd.NewCommand("checkout", "-b").AddDynamicArguments(forkHeadRef).RunStdString(t.Context(), &gitcmd.RunOpts{Dir: forkRepo.RepoPath()}) assert.NoError(t, err) assert.NoError(t, os.WriteFile(filepath.Join(forkRepo.RepoPath(), "README.md"), []byte("# branch2 "+forkRepo.RepoPath()), 0o644)) assert.NoError(t, git.AddChanges(t.Context(), forkRepo.RepoPath(), true)) diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index b9000f27034..6a87aa8a274 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -13,6 +13,7 @@ import ( system_model "code.gitea.io/gitea/models/system" "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" giturl "code.gitea.io/gitea/modules/git/url" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/globallock" @@ -255,7 +256,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo log.Trace("SyncMirrors [repo: %-v]: running git remote update...", m.Repo) // use fetch but not remote update because git fetch support --tags but remote update doesn't - cmd := git.NewCommand("fetch") + cmd := gitcmd.NewCommand("fetch") if m.EnablePrune { cmd.AddArguments("--prune") } @@ -271,7 +272,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo stdoutBuilder := strings.Builder{} stderrBuilder := strings.Builder{} - if err := cmd.Run(ctx, &git.RunOpts{ + if err := cmd.Run(ctx, &gitcmd.RunOpts{ Timeout: timeout, Dir: repoPath, Env: envs, @@ -296,7 +297,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo // Successful prune - reattempt mirror stderrBuilder.Reset() stdoutBuilder.Reset() - if err = cmd.Run(ctx, &git.RunOpts{ + if err = cmd.Run(ctx, &gitcmd.RunOpts{ Timeout: timeout, Dir: repoPath, Stdout: &stdoutBuilder, diff --git a/services/org/org.go b/services/org/org.go index 142a643f9fc..8da77c691c1 100644 --- a/services/org/org.go +++ b/services/org/org.go @@ -52,39 +52,34 @@ func deleteOrganization(ctx context.Context, org *org_model.Organization) error // DeleteOrganization completely and permanently deletes everything of organization. func DeleteOrganization(ctx context.Context, org *org_model.Organization, purge bool) error { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - - if purge { - err := repo_service.DeleteOwnerRepositoriesDirectly(ctx, org.AsUser()) - if err != nil { - return err + if err := db.WithTx(ctx, func(ctx context.Context) error { + if purge { + err := repo_service.DeleteOwnerRepositoriesDirectly(ctx, org.AsUser()) + if err != nil { + return err + } } - } - // Check ownership of repository. - count, err := repo_model.CountRepositories(ctx, repo_model.CountRepositoryOptions{OwnerID: org.ID}) - if err != nil { - return fmt.Errorf("GetRepositoryCount: %w", err) - } else if count > 0 { - return repo_model.ErrUserOwnRepos{UID: org.ID} - } + // Check ownership of repository. + count, err := repo_model.CountRepositories(ctx, repo_model.CountRepositoryOptions{OwnerID: org.ID}) + if err != nil { + return fmt.Errorf("GetRepositoryCount: %w", err) + } else if count > 0 { + return repo_model.ErrUserOwnRepos{UID: org.ID} + } - // Check ownership of packages. - if ownsPackages, err := packages_model.HasOwnerPackages(ctx, org.ID); err != nil { - return fmt.Errorf("HasOwnerPackages: %w", err) - } else if ownsPackages { - return packages_model.ErrUserOwnPackages{UID: org.ID} - } + // Check ownership of packages. + if ownsPackages, err := packages_model.HasOwnerPackages(ctx, org.ID); err != nil { + return fmt.Errorf("HasOwnerPackages: %w", err) + } else if ownsPackages { + return packages_model.ErrUserOwnPackages{UID: org.ID} + } - if err := deleteOrganization(ctx, org); err != nil { - return fmt.Errorf("DeleteOrganization: %w", err) - } - - if err := committer.Commit(); err != nil { + if err := deleteOrganization(ctx, org); err != nil { + return fmt.Errorf("DeleteOrganization: %w", err) + } + return nil + }); err != nil { return err } diff --git a/services/org/user.go b/services/org/user.go index 26927253d25..68582718582 100644 --- a/services/org/user.go +++ b/services/org/user.go @@ -47,57 +47,52 @@ func RemoveOrgUser(ctx context.Context, org *organization.Organization, user *us } } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() + return db.WithTx(ctx, func(ctx context.Context) error { + if _, err := db.DeleteByID[organization.OrgUser](ctx, ou.ID); err != nil { + return err + } else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members=num_members-1 WHERE id=?", org.ID); err != nil { + return err + } - if _, err := db.DeleteByID[organization.OrgUser](ctx, ou.ID); err != nil { - return err - } else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members=num_members-1 WHERE id=?", org.ID); err != nil { - return err - } + // Delete all repository accesses and unwatch them. + env, err := repo_model.AccessibleReposEnv(ctx, org, user.ID) + if err != nil { + return fmt.Errorf("AccessibleReposEnv: %w", err) + } + repoIDs, err := env.RepoIDs(ctx) + if err != nil { + return fmt.Errorf("GetUserRepositories [%d]: %w", user.ID, err) + } - // Delete all repository accesses and unwatch them. - env, err := repo_model.AccessibleReposEnv(ctx, org, user.ID) - if err != nil { - return fmt.Errorf("AccessibleReposEnv: %w", err) - } - repoIDs, err := env.RepoIDs(ctx) - if err != nil { - return fmt.Errorf("GetUserRepositories [%d]: %w", user.ID, err) - } + for _, repoID := range repoIDs { + repo, err := repo_model.GetRepositoryByID(ctx, repoID) + if err != nil { + return err + } + if err = repo_model.WatchRepo(ctx, user, repo, false); err != nil { + return err + } + } - for _, repoID := range repoIDs { - repo, err := repo_model.GetRepositoryByID(ctx, repoID) + if len(repoIDs) > 0 { + if _, err = db.GetEngine(ctx). + Where("user_id = ?", user.ID). + In("repo_id", repoIDs). + Delete(new(access_model.Access)); err != nil { + return err + } + } + + // Delete member in their teams. + teams, err := organization.GetUserOrgTeams(ctx, org.ID, user.ID) if err != nil { return err } - if err = repo_model.WatchRepo(ctx, user, repo, false); err != nil { - return err + for _, t := range teams { + if err = removeTeamMember(ctx, t, user); err != nil { + return err + } } - } - - if len(repoIDs) > 0 { - if _, err = db.GetEngine(ctx). - Where("user_id = ?", user.ID). - In("repo_id", repoIDs). - Delete(new(access_model.Access)); err != nil { - return err - } - } - - // Delete member in their teams. - teams, err := organization.GetUserOrgTeams(ctx, org.ID, user.ID) - if err != nil { - return err - } - for _, t := range teams { - if err = removeTeamMember(ctx, t, user); err != nil { - return err - } - } - - return committer.Commit() + return nil + }) } diff --git a/services/pull/check.go b/services/pull/check.go index 7fcec22f492..c2ee49b6bb4 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/globallock" "code.gitea.io/gitea/modules/graceful" @@ -280,9 +281,9 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com prHeadRef := pr.GetGitHeadRefName() // Check if the pull request is merged into BaseBranch - if _, _, err := git.NewCommand("merge-base", "--is-ancestor"). + if _, _, err := gitcmd.NewCommand("merge-base", "--is-ancestor"). AddDynamicArguments(prHeadRef, pr.BaseBranch). - RunStdString(ctx, &git.RunOpts{Dir: pr.BaseRepo.RepoPath()}); err != nil { + RunStdString(ctx, &gitcmd.RunOpts{Dir: pr.BaseRepo.RepoPath()}); err != nil { if strings.Contains(err.Error(), "exit status 1") { // prHeadRef is not an ancestor of the base branch return nil, nil @@ -308,9 +309,9 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName) // Get the commit from BaseBranch where the pull request got merged - mergeCommit, _, err := git.NewCommand("rev-list", "--ancestry-path", "--merges", "--reverse"). + mergeCommit, _, err := gitcmd.NewCommand("rev-list", "--ancestry-path", "--merges", "--reverse"). AddDynamicArguments(prHeadCommitID+".."+pr.BaseBranch). - RunStdString(ctx, &git.RunOpts{Dir: pr.BaseRepo.RepoPath()}) + RunStdString(ctx, &gitcmd.RunOpts{Dir: pr.BaseRepo.RepoPath()}) if err != nil { return nil, fmt.Errorf("git rev-list --ancestry-path --merges --reverse: %w", err) } else if len(mergeCommit) < objectFormat.FullLength() { diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go index d15d3181497..e7c14a5e7aa 100644 --- a/services/pull/commit_status.go +++ b/services/pull/commit_status.go @@ -12,9 +12,9 @@ import ( issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/modules/commitstatus" "code.gitea.io/gitea/modules/gitrepo" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/log" - "github.com/gobwas/glob" "github.com/pkg/errors" ) diff --git a/services/pull/merge.go b/services/pull/merge.go index a941c204357..4d6afb8d338 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -26,6 +26,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/globallock" "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/log" @@ -400,7 +401,7 @@ func doMergeAndPush(ctx context.Context, pr *issues_model.PullRequest, doer *use ) mergeCtx.env = append(mergeCtx.env, repo_module.EnvPushTrigger+"="+string(pushTrigger)) - pushCmd := git.NewCommand("push", "origin").AddDynamicArguments(baseBranch + ":" + git.BranchPrefix + pr.BaseBranch) + pushCmd := gitcmd.NewCommand("push", "origin").AddDynamicArguments(baseBranch + ":" + git.BranchPrefix + pr.BaseBranch) // Push back to upstream. // This cause an api call to "/api/internal/hook/post-receive/...", @@ -430,7 +431,7 @@ func doMergeAndPush(ctx context.Context, pr *issues_model.PullRequest, doer *use } func commitAndSignNoAuthor(ctx *mergeContext, message string) error { - cmdCommit := git.NewCommand("commit").AddOptionFormat("--message=%s", message) + cmdCommit := gitcmd.NewCommand("commit").AddOptionFormat("--message=%s", message) if ctx.signKey == nil { cmdCommit.AddArguments("--no-gpg-sign") } else { @@ -499,7 +500,7 @@ func (err ErrMergeDivergingFastForwardOnly) Error() string { return fmt.Sprintf("Merge DivergingFastForwardOnly Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut) } -func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *git.Command) error { +func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *gitcmd.Command) error { if err := cmd.Run(ctx, ctx.RunOpts()); err != nil { // Merge will leave a MERGE_HEAD file in the .git folder if there is a conflict if _, statErr := os.Stat(filepath.Join(ctx.tmpBasePath, ".git", "MERGE_HEAD")); statErr == nil { diff --git a/services/pull/merge_ff_only.go b/services/pull/merge_ff_only.go index 6c3a68b95b3..22a560e29ca 100644 --- a/services/pull/merge_ff_only.go +++ b/services/pull/merge_ff_only.go @@ -5,13 +5,13 @@ package pull import ( repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" ) // doMergeStyleFastForwardOnly merges the tracking into the current HEAD - which is assumed to be staging branch (equal to the pr.BaseBranch) func doMergeStyleFastForwardOnly(ctx *mergeContext) error { - cmd := git.NewCommand("merge", "--ff-only").AddDynamicArguments(trackingBranch) + cmd := gitcmd.NewCommand("merge", "--ff-only").AddDynamicArguments(trackingBranch) if err := runMergeCommand(ctx, repo_model.MergeStyleFastForwardOnly, cmd); err != nil { log.Error("%-v Unable to merge tracking into base: %v", ctx.pr, err) return err diff --git a/services/pull/merge_merge.go b/services/pull/merge_merge.go index 118d21c7cd9..bc94400f216 100644 --- a/services/pull/merge_merge.go +++ b/services/pull/merge_merge.go @@ -5,13 +5,13 @@ package pull import ( repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" ) // doMergeStyleMerge merges the tracking branch into the current HEAD - which is assumed to be the staging branch (equal to the pr.BaseBranch) func doMergeStyleMerge(ctx *mergeContext, message string) error { - cmd := git.NewCommand("merge", "--no-ff", "--no-commit").AddDynamicArguments(trackingBranch) + cmd := gitcmd.NewCommand("merge", "--no-ff", "--no-commit").AddDynamicArguments(trackingBranch) if err := runMergeCommand(ctx, repo_model.MergeStyleMerge, cmd); err != nil { log.Error("%-v Unable to merge tracking into base: %v", ctx.pr, err) return err diff --git a/services/pull/merge_prepare.go b/services/pull/merge_prepare.go index 31a1e137344..d7152560d21 100644 --- a/services/pull/merge_prepare.go +++ b/services/pull/merge_prepare.go @@ -18,6 +18,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" asymkey_service "code.gitea.io/gitea/services/asymkey" ) @@ -31,10 +32,10 @@ type mergeContext struct { env []string } -func (ctx *mergeContext) RunOpts() *git.RunOpts { +func (ctx *mergeContext) RunOpts() *gitcmd.RunOpts { ctx.outbuf.Reset() ctx.errbuf.Reset() - return &git.RunOpts{ + return &gitcmd.RunOpts{ Env: ctx.env, Dir: ctx.tmpBasePath, Stdout: ctx.outbuf, @@ -73,7 +74,7 @@ func createTemporaryRepoForMerge(ctx context.Context, pr *issues_model.PullReque } if expectedHeadCommitID != "" { - trackingCommitID, _, err := git.NewCommand("show-ref", "--hash").AddDynamicArguments(git.BranchPrefix+trackingBranch).RunStdString(ctx, &git.RunOpts{Dir: mergeCtx.tmpBasePath}) + trackingCommitID, _, err := gitcmd.NewCommand("show-ref", "--hash").AddDynamicArguments(git.BranchPrefix+trackingBranch).RunStdString(ctx, &gitcmd.RunOpts{Dir: mergeCtx.tmpBasePath}) if err != nil { defer cancel() log.Error("failed to get sha of head branch in %-v: show-ref[%s] --hash refs/heads/tracking: %v", mergeCtx.pr, mergeCtx.tmpBasePath, err) @@ -151,7 +152,7 @@ func prepareTemporaryRepoForMerge(ctx *mergeContext) error { } setConfig := func(key, value string) error { - if err := git.NewCommand("config", "--local").AddDynamicArguments(key, value). + if err := gitcmd.NewCommand("config", "--local").AddDynamicArguments(key, value). Run(ctx, ctx.RunOpts()); err != nil { log.Error("git config [%s -> %q]: %v\n%s\n%s", key, value, err, ctx.outbuf.String(), ctx.errbuf.String()) return fmt.Errorf("git config [%s -> %q]: %w\n%s\n%s", key, value, err, ctx.outbuf.String(), ctx.errbuf.String()) @@ -184,7 +185,7 @@ func prepareTemporaryRepoForMerge(ctx *mergeContext) error { } // Read base branch index - if err := git.NewCommand("read-tree", "HEAD"). + if err := gitcmd.NewCommand("read-tree", "HEAD"). Run(ctx, ctx.RunOpts()); err != nil { log.Error("git read-tree HEAD: %v\n%s\n%s", err, ctx.outbuf.String(), ctx.errbuf.String()) return fmt.Errorf("Unable to read base branch in to the index: %w\n%s\n%s", err, ctx.outbuf.String(), ctx.errbuf.String()) @@ -221,8 +222,8 @@ func getDiffTree(ctx context.Context, repoPath, baseBranch, headBranch string, o return 0, nil, nil } - err = git.NewCommand("diff-tree", "--no-commit-id", "--name-only", "-r", "-r", "-z", "--root").AddDynamicArguments(baseBranch, headBranch). - Run(ctx, &git.RunOpts{ + err = gitcmd.NewCommand("diff-tree", "--no-commit-id", "--name-only", "-r", "-r", "-z", "--root").AddDynamicArguments(baseBranch, headBranch). + Run(ctx, &gitcmd.RunOpts{ Dir: repoPath, Stdout: diffOutWriter, PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { @@ -272,7 +273,7 @@ func (err ErrRebaseConflicts) Error() string { // if there is a conflict it will return an ErrRebaseConflicts func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle) error { // Checkout head branch - if err := git.NewCommand("checkout", "-b").AddDynamicArguments(stagingBranch, trackingBranch). + if err := gitcmd.NewCommand("checkout", "-b").AddDynamicArguments(stagingBranch, trackingBranch). Run(ctx, ctx.RunOpts()); err != nil { return fmt.Errorf("unable to git checkout tracking as staging in temp repo for %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String()) } @@ -280,7 +281,7 @@ func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle) ctx.errbuf.Reset() // Rebase before merging - if err := git.NewCommand("rebase").AddDynamicArguments(baseBranch). + if err := gitcmd.NewCommand("rebase").AddDynamicArguments(baseBranch). Run(ctx, ctx.RunOpts()); err != nil { // Rebase will leave a REBASE_HEAD file in .git if there is a conflict if _, statErr := os.Stat(filepath.Join(ctx.tmpBasePath, ".git", "REBASE_HEAD")); statErr == nil { diff --git a/services/pull/merge_rebase.go b/services/pull/merge_rebase.go index dd7c8761f00..1d7395ebca1 100644 --- a/services/pull/merge_rebase.go +++ b/services/pull/merge_rebase.go @@ -9,6 +9,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" ) @@ -16,7 +17,7 @@ import ( // getRebaseAmendMessage composes the message to amend commits in rebase merge of a pull request. func getRebaseAmendMessage(ctx *mergeContext, baseGitRepo *git.Repository) (message string, err error) { // Get existing commit message. - commitMessage, _, err := git.NewCommand("show", "--format=%B", "-s").RunStdString(ctx, &git.RunOpts{Dir: ctx.tmpBasePath}) + commitMessage, _, err := gitcmd.NewCommand("show", "--format=%B", "-s").RunStdString(ctx, &gitcmd.RunOpts{Dir: ctx.tmpBasePath}) if err != nil { return "", err } @@ -42,7 +43,7 @@ func doMergeRebaseFastForward(ctx *mergeContext) error { return fmt.Errorf("Failed to get full commit id for HEAD: %w", err) } - cmd := git.NewCommand("merge", "--ff-only").AddDynamicArguments(stagingBranch) + cmd := gitcmd.NewCommand("merge", "--ff-only").AddDynamicArguments(stagingBranch) if err := runMergeCommand(ctx, repo_model.MergeStyleRebase, cmd); err != nil { log.Error("Unable to merge staging into base: %v", err) return err @@ -73,7 +74,7 @@ func doMergeRebaseFastForward(ctx *mergeContext) error { } if newMessage != "" { - if err := git.NewCommand("commit", "--amend").AddOptionFormat("--message=%s", newMessage).Run(ctx, &git.RunOpts{Dir: ctx.tmpBasePath}); err != nil { + if err := gitcmd.NewCommand("commit", "--amend").AddOptionFormat("--message=%s", newMessage).Run(ctx, &gitcmd.RunOpts{Dir: ctx.tmpBasePath}); err != nil { log.Error("Unable to amend commit message: %v", err) return err } @@ -84,7 +85,7 @@ func doMergeRebaseFastForward(ctx *mergeContext) error { // Perform rebase merge with merge commit. func doMergeRebaseMergeCommit(ctx *mergeContext, message string) error { - cmd := git.NewCommand("merge").AddArguments("--no-ff", "--no-commit").AddDynamicArguments(stagingBranch) + cmd := gitcmd.NewCommand("merge").AddArguments("--no-ff", "--no-commit").AddDynamicArguments(stagingBranch) if err := runMergeCommand(ctx, repo_model.MergeStyleRebaseMerge, cmd); err != nil { log.Error("Unable to merge staging into base: %v", err) @@ -105,7 +106,7 @@ func doMergeStyleRebase(ctx *mergeContext, mergeStyle repo_model.MergeStyle, mes } // Checkout base branch again - if err := git.NewCommand("checkout").AddDynamicArguments(baseBranch). + if err := gitcmd.NewCommand("checkout").AddDynamicArguments(baseBranch). Run(ctx, ctx.RunOpts()); err != nil { log.Error("git checkout base prior to merge post staging rebase %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String()) return fmt.Errorf("git checkout base prior to merge post staging rebase %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String()) diff --git a/services/pull/merge_squash.go b/services/pull/merge_squash.go index 0049c0b117d..99c4311e045 100644 --- a/services/pull/merge_squash.go +++ b/services/pull/merge_squash.go @@ -10,6 +10,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" ) @@ -57,7 +58,7 @@ func doMergeStyleSquash(ctx *mergeContext, message string) error { return fmt.Errorf("getAuthorSignatureSquash: %w", err) } - cmdMerge := git.NewCommand("merge", "--squash").AddDynamicArguments(trackingBranch) + cmdMerge := gitcmd.NewCommand("merge", "--squash").AddDynamicArguments(trackingBranch) if err := runMergeCommand(ctx, repo_model.MergeStyleSquash, cmdMerge); err != nil { log.Error("%-v Unable to merge --squash tracking into base: %v", ctx.pr, err) return err @@ -68,7 +69,7 @@ func doMergeStyleSquash(ctx *mergeContext, message string) error { message = AddCommitMessageTailer(message, "Co-authored-by", sig.String()) message = AddCommitMessageTailer(message, "Co-committed-by", sig.String()) // FIXME: this one should be removed, it is not really used or widely used } - cmdCommit := git.NewCommand("commit"). + cmdCommit := gitcmd.NewCommand("commit"). AddOptionFormat("--author='%s <%s>'", sig.Name, sig.Email). AddOptionFormat("--message=%s", message) if ctx.signKey == nil { diff --git a/services/pull/patch.go b/services/pull/patch.go index 9d9b8d0d076..674ae012535 100644 --- a/services/pull/patch.go +++ b/services/pull/patch.go @@ -18,14 +18,14 @@ import ( "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" - - "github.com/gobwas/glob" ) // DownloadDiffOrPatch will write the patch for the pr to the writer @@ -91,7 +91,7 @@ func testPullRequestTmpRepoBranchMergeable(ctx context.Context, prCtx *prTmpRepo defer gitRepo.Close() // 1. update merge base - pr.MergeBase, _, err = git.NewCommand("merge-base", "--", "base", "tracking").RunStdString(ctx, &git.RunOpts{Dir: prCtx.tmpBasePath}) + pr.MergeBase, _, err = gitcmd.NewCommand("merge-base", "--", "base", "tracking").RunStdString(ctx, &gitcmd.RunOpts{Dir: prCtx.tmpBasePath}) if err != nil { var err2 error pr.MergeBase, err2 = gitRepo.GetRefCommitID(git.BranchPrefix + "base") @@ -191,7 +191,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f } // Need to get the objects from the object db to attempt to merge - root, _, err := git.NewCommand("unpack-file").AddDynamicArguments(file.stage1.sha).RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath}) + root, _, err := gitcmd.NewCommand("unpack-file").AddDynamicArguments(file.stage1.sha).RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpBasePath}) if err != nil { return fmt.Errorf("unable to get root object: %s at path: %s for merging. Error: %w", file.stage1.sha, file.stage1.path, err) } @@ -200,7 +200,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f _ = util.Remove(filepath.Join(tmpBasePath, root)) }() - base, _, err := git.NewCommand("unpack-file").AddDynamicArguments(file.stage2.sha).RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath}) + base, _, err := gitcmd.NewCommand("unpack-file").AddDynamicArguments(file.stage2.sha).RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpBasePath}) if err != nil { return fmt.Errorf("unable to get base object: %s at path: %s for merging. Error: %w", file.stage2.sha, file.stage2.path, err) } @@ -208,7 +208,7 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f defer func() { _ = util.Remove(base) }() - head, _, err := git.NewCommand("unpack-file").AddDynamicArguments(file.stage3.sha).RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath}) + head, _, err := gitcmd.NewCommand("unpack-file").AddDynamicArguments(file.stage3.sha).RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpBasePath}) if err != nil { return fmt.Errorf("unable to get head object:%s at path: %s for merging. Error: %w", file.stage3.sha, file.stage3.path, err) } @@ -218,13 +218,13 @@ func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, f }() // now git merge-file annoyingly takes a different order to the merge-tree ... - _, _, conflictErr := git.NewCommand("merge-file").AddDynamicArguments(base, root, head).RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath}) + _, _, conflictErr := gitcmd.NewCommand("merge-file").AddDynamicArguments(base, root, head).RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpBasePath}) if conflictErr != nil { return &errMergeConflict{file.stage2.path} } // base now contains the merged data - hash, _, err := git.NewCommand("hash-object", "-w", "--path").AddDynamicArguments(file.stage2.path, base).RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath}) + hash, _, err := gitcmd.NewCommand("hash-object", "-w", "--path").AddDynamicArguments(file.stage2.path, base).RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpBasePath}) if err != nil { return err } @@ -249,7 +249,7 @@ func AttemptThreeWayMerge(ctx context.Context, gitPath string, gitRepo *git.Repo defer cancel() // First we use read-tree to do a simple three-way merge - if _, _, err := git.NewCommand("read-tree", "-m").AddDynamicArguments(base, ours, theirs).RunStdString(ctx, &git.RunOpts{Dir: gitPath}); err != nil { + if _, _, err := gitcmd.NewCommand("read-tree", "-m").AddDynamicArguments(base, ours, theirs).RunStdString(ctx, &gitcmd.RunOpts{Dir: gitPath}); err != nil { log.Error("Unable to run read-tree -m! Error: %v", err) return false, nil, fmt.Errorf("unable to run read-tree -m! Error: %w", err) } @@ -323,9 +323,9 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo * // No conflicts detected so we need to check if the patch is empty... // a. Write the newly merged tree and check the new tree-hash var treeHash string - treeHash, _, err = git.NewCommand("write-tree").RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath}) + treeHash, _, err = gitcmd.NewCommand("write-tree").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpBasePath}) if err != nil { - lsfiles, _, _ := git.NewCommand("ls-files", "-u").RunStdString(ctx, &git.RunOpts{Dir: tmpBasePath}) + lsfiles, _, _ := gitcmd.NewCommand("ls-files", "-u").RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpBasePath}) return false, fmt.Errorf("unable to write unconflicted tree: %w\n`git ls-files -u`:\n%s", err, lsfiles) } treeHash = strings.TrimSpace(treeHash) @@ -382,7 +382,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo * log.Trace("PullRequest[%d].testPullRequestTmpRepoBranchMergeable (patchPath): %s", pr.ID, patchPath) // 4. Read the base branch in to the index of the temporary repository - _, _, err = git.NewCommand("read-tree", "base").RunStdString(gitRepo.Ctx, &git.RunOpts{Dir: tmpBasePath}) + _, _, err = gitcmd.NewCommand("read-tree", "base").RunStdString(gitRepo.Ctx, &gitcmd.RunOpts{Dir: tmpBasePath}) if err != nil { return false, fmt.Errorf("git read-tree %s: %w", pr.BaseBranch, err) } @@ -395,7 +395,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo * prConfig := prUnit.PullRequestsConfig() // 6. Prepare the arguments to apply the patch against the index - cmdApply := git.NewCommand("apply", "--check", "--cached") + cmdApply := gitcmd.NewCommand("apply", "--check", "--cached") if prConfig.IgnoreWhitespaceConflicts { cmdApply.AddArguments("--ignore-whitespace") } @@ -426,7 +426,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo * // 8. Run the check command conflict = false - err = cmdApply.Run(gitRepo.Ctx, &git.RunOpts{ + err = cmdApply.Run(gitRepo.Ctx, &gitcmd.RunOpts{ Dir: tmpBasePath, Stderr: stderrWriter, PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { diff --git a/services/pull/patch_unmerged.go b/services/pull/patch_unmerged.go index 200d2233e93..91640354807 100644 --- a/services/pull/patch_unmerged.go +++ b/services/pull/patch_unmerged.go @@ -13,7 +13,7 @@ import ( "strconv" "strings" - "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" ) @@ -72,8 +72,8 @@ func readUnmergedLsFileLines(ctx context.Context, tmpBasePath string, outputChan }() stderr := &strings.Builder{} - err = git.NewCommand("ls-files", "-u", "-z"). - Run(ctx, &git.RunOpts{ + err = gitcmd.NewCommand("ls-files", "-u", "-z"). + Run(ctx, &gitcmd.RunOpts{ Dir: tmpBasePath, Stdout: lsFilesWriter, Stderr: stderr, @@ -116,7 +116,7 @@ func readUnmergedLsFileLines(ctx context.Context, tmpBasePath string, outputChan }, }) if err != nil { - outputChan <- &lsFileLine{err: fmt.Errorf("git ls-files -u -z: %w", git.ConcatenateError(err, stderr.String()))} + outputChan <- &lsFileLine{err: fmt.Errorf("git ls-files -u -z: %w", gitcmd.ConcatenateError(err, stderr.String()))} } } diff --git a/services/pull/pull.go b/services/pull/pull.go index 8144aee1b0c..51a22171ef4 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -25,6 +25,7 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/globallock" "code.gitea.io/gitea/modules/graceful" @@ -107,13 +108,6 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error { assigneeCommentMap := make(map[int64]*issues_model.Comment) - // add first push codes comment - baseGitRepo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo) - if err != nil { - return err - } - defer baseGitRepo.Close() - permDoer, err := access_model.GetUserRepoPermission(ctx, repo, issue.Poster) if err != nil { return err @@ -145,6 +139,7 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error { return err } + // add first push codes comment if _, err := CreatePushPullComment(ctx, issue.Poster, pr, git.BranchPrefix+pr.BaseBranch, pr.GetGitHeadRefName(), false); err != nil { return err } @@ -198,12 +193,11 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error { return nil }); err != nil { // cleanup: this will only remove the reference, the real commit will be clean up when next GC - if err1 := baseGitRepo.RemoveReference(pr.GetGitHeadRefName()); err1 != nil { - log.Error("RemoveReference: %v", err1) + if err1 := gitrepo.RemoveRef(ctx, pr.BaseRepo, pr.GetGitHeadRefName()); err1 != nil { + log.Error("RemoveRef: %v", err1) } return err } - baseGitRepo.Close() // close immediately to avoid notifications will open the repository again reviewRequestNotify(ctx, issue, issue.Poster, reviewNotifiers) @@ -546,14 +540,14 @@ func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest, return false, fmt.Errorf("GetMergeBase: %w", err) } - cmd := git.NewCommand("diff", "--name-only", "-z").AddDynamicArguments(newCommitID, oldCommitID, base) + cmd := gitcmd.NewCommand("diff", "--name-only", "-z").AddDynamicArguments(newCommitID, oldCommitID, base) stdoutReader, stdoutWriter, err := os.Pipe() if err != nil { return false, fmt.Errorf("unable to open pipe for to run diff: %w", err) } stderr := new(bytes.Buffer) - if err := cmd.Run(ctx, &git.RunOpts{ + if err := cmd.Run(ctx, &gitcmd.RunOpts{ Dir: prCtx.tmpBasePath, Stdout: stdoutWriter, Stderr: stderr, @@ -568,7 +562,7 @@ func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest, if err == util.ErrNotEmpty { return true, nil } - err = git.ConcatenateError(err, stderr.String()) + err = gitcmd.ConcatenateError(err, stderr.String()) log.Error("Unable to run diff on %s %s %s in tempRepo for PR[%d]%s/%s...%s/%s: Error: %v", newCommitID, oldCommitID, base, @@ -670,8 +664,7 @@ func UpdateRef(ctx context.Context, pr *issues_model.PullRequest) (err error) { return err } - _, _, err = git.NewCommand("update-ref").AddDynamicArguments(pr.GetGitHeadRefName(), pr.HeadCommitID).RunStdString(ctx, &git.RunOpts{Dir: pr.BaseRepo.RepoPath()}) - if err != nil { + if err := gitrepo.UpdateRef(ctx, pr.BaseRepo, pr.GetGitHeadRefName(), pr.HeadCommitID); err != nil { log.Error("Unable to update ref in base repository for PR[%d] Error: %v", pr.ID, err) } diff --git a/services/pull/temp_repo.go b/services/pull/temp_repo.go index 89f150fd92e..8750b4288a4 100644 --- a/services/pull/temp_repo.go +++ b/services/pull/temp_repo.go @@ -15,6 +15,7 @@ import ( issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" @@ -36,10 +37,10 @@ type prTmpRepoContext struct { errbuf *strings.Builder // any use should be preceded by a Reset and preferably after use } -func (ctx *prTmpRepoContext) RunOpts() *git.RunOpts { +func (ctx *prTmpRepoContext) RunOpts() *gitcmd.RunOpts { ctx.outbuf.Reset() ctx.errbuf.Reset() - return &git.RunOpts{ + return &gitcmd.RunOpts{ Dir: ctx.tmpBasePath, Stdout: ctx.outbuf, Stderr: ctx.errbuf, @@ -101,7 +102,7 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest) remoteRepoName := "head_repo" baseBranch := "base" - fetchArgs := git.TrustedCmdArgs{"--no-tags"} + fetchArgs := gitcmd.TrustedCmdArgs{"--no-tags"} if git.DefaultFeatures().CheckVersionAtLeast("2.25.0") { // Writing the commit graph can be slow and is not needed here fetchArgs = append(fetchArgs, "--no-write-commit-graph") @@ -131,21 +132,21 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest) return nil, nil, fmt.Errorf("Unable to add base repository to temporary repo [%s -> tmpBasePath]: %w", pr.BaseRepo.FullName(), err) } - if err := git.NewCommand("remote", "add", "-t").AddDynamicArguments(pr.BaseBranch).AddArguments("-m").AddDynamicArguments(pr.BaseBranch).AddDynamicArguments("origin", baseRepoPath). + if err := gitcmd.NewCommand("remote", "add", "-t").AddDynamicArguments(pr.BaseBranch).AddArguments("-m").AddDynamicArguments(pr.BaseBranch).AddDynamicArguments("origin", baseRepoPath). Run(ctx, prCtx.RunOpts()); err != nil { log.Error("%-v Unable to add base repository as origin [%s -> %s]: %v\n%s\n%s", pr, pr.BaseRepo.FullName(), tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String()) cancel() return nil, nil, fmt.Errorf("Unable to add base repository as origin [%s -> tmpBasePath]: %w\n%s\n%s", pr.BaseRepo.FullName(), err, prCtx.outbuf.String(), prCtx.errbuf.String()) } - if err := git.NewCommand("fetch", "origin").AddArguments(fetchArgs...).AddDashesAndList(pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch). + if err := gitcmd.NewCommand("fetch", "origin").AddArguments(fetchArgs...).AddDashesAndList(pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch). Run(ctx, prCtx.RunOpts()); err != nil { log.Error("%-v Unable to fetch origin base branch [%s:%s -> base, original_base in %s]: %v:\n%s\n%s", pr, pr.BaseRepo.FullName(), pr.BaseBranch, tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String()) cancel() return nil, nil, fmt.Errorf("Unable to fetch origin base branch [%s:%s -> base, original_base in tmpBasePath]: %w\n%s\n%s", pr.BaseRepo.FullName(), pr.BaseBranch, err, prCtx.outbuf.String(), prCtx.errbuf.String()) } - if err := git.NewCommand("symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseBranch). + if err := gitcmd.NewCommand("symbolic-ref").AddDynamicArguments("HEAD", git.BranchPrefix+baseBranch). Run(ctx, prCtx.RunOpts()); err != nil { log.Error("%-v Unable to set HEAD as base branch in [%s]: %v\n%s\n%s", pr, tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String()) cancel() @@ -158,7 +159,7 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest) return nil, nil, fmt.Errorf("Unable to add head base repository to temporary repo [%s -> tmpBasePath]: %w", pr.HeadRepo.FullName(), err) } - if err := git.NewCommand("remote", "add").AddDynamicArguments(remoteRepoName, headRepoPath). + if err := gitcmd.NewCommand("remote", "add").AddDynamicArguments(remoteRepoName, headRepoPath). Run(ctx, prCtx.RunOpts()); err != nil { log.Error("%-v Unable to add head repository as head_repo [%s -> %s]: %v\n%s\n%s", pr, pr.HeadRepo.FullName(), tmpBasePath, err, prCtx.outbuf.String(), prCtx.errbuf.String()) cancel() @@ -176,7 +177,7 @@ func createTemporaryRepoForPR(ctx context.Context, pr *issues_model.PullRequest) } else { headBranch = pr.GetGitHeadRefName() } - if err := git.NewCommand("fetch").AddArguments(fetchArgs...).AddDynamicArguments(remoteRepoName, headBranch+":"+trackingBranch). + if err := gitcmd.NewCommand("fetch").AddArguments(fetchArgs...).AddDynamicArguments(remoteRepoName, headBranch+":"+trackingBranch). Run(ctx, prCtx.RunOpts()); err != nil { cancel() if !gitrepo.IsBranchExist(ctx, pr.HeadRepo, pr.HeadBranch) { diff --git a/services/pull/update_rebase.go b/services/pull/update_rebase.go index 9ff062f99c6..3b5d3afba5b 100644 --- a/services/pull/update_rebase.go +++ b/services/pull/update_rebase.go @@ -12,6 +12,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" @@ -27,7 +28,7 @@ func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullReques defer cancel() // Determine the old merge-base before the rebase - we use this for LFS push later on - oldMergeBase, _, _ := git.NewCommand("merge-base").AddDashesAndList(baseBranch, trackingBranch).RunStdString(ctx, &git.RunOpts{Dir: mergeCtx.tmpBasePath}) + oldMergeBase, _, _ := gitcmd.NewCommand("merge-base").AddDashesAndList(baseBranch, trackingBranch).RunStdString(ctx, &gitcmd.RunOpts{Dir: mergeCtx.tmpBasePath}) oldMergeBase = strings.TrimSpace(oldMergeBase) // Rebase the tracking branch on to the base as the staging branch @@ -62,7 +63,7 @@ func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullReques headUser = pr.HeadRepo.Owner } - pushCmd := git.NewCommand("push", "-f", "head_repo"). + pushCmd := gitcmd.NewCommand("push", "-f", "head_repo"). AddDynamicArguments(stagingBranch + ":" + git.BranchPrefix + pr.HeadBranch) // Push back to the head repository. @@ -71,7 +72,7 @@ func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullReques mergeCtx.outbuf.Reset() mergeCtx.errbuf.Reset() - if err := pushCmd.Run(ctx, &git.RunOpts{ + if err := pushCmd.Run(ctx, &gitcmd.RunOpts{ Env: repo_module.FullPushingEnvironment( headUser, doer, diff --git a/services/release/release.go b/services/release/release.go index 0b8a74252a0..3c5392c9938 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -15,6 +15,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" @@ -267,74 +268,69 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo } rel.LowerTagName = strings.ToLower(rel.TagName) - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - oldRelease, err := repo_model.GetReleaseByID(ctx, rel.ID) if err != nil { return err } isConvertedFromTag := oldRelease.IsTag && !rel.IsTag - if err = repo_model.UpdateRelease(ctx, rel); err != nil { - return err - } - - if err = repo_model.AddReleaseAttachments(ctx, rel.ID, addAttachmentUUIDs); err != nil { - return fmt.Errorf("AddReleaseAttachments: %w", err) - } - - deletedUUIDs := make(container.Set[string]) - if len(delAttachmentUUIDs) > 0 { - // Check attachments - attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, delAttachmentUUIDs) - if err != nil { - return fmt.Errorf("GetAttachmentsByUUIDs [uuids: %v]: %w", delAttachmentUUIDs, err) + if err := db.WithTx(ctx, func(ctx context.Context) error { + if err = repo_model.UpdateRelease(ctx, rel); err != nil { + return err } - for _, attach := range attachments { - if attach.ReleaseID != rel.ID { - return util.NewPermissionDeniedErrorf("delete attachment of release permission denied") + + if err = repo_model.AddReleaseAttachments(ctx, rel.ID, addAttachmentUUIDs); err != nil { + return fmt.Errorf("AddReleaseAttachments: %w", err) + } + + deletedUUIDs := make(container.Set[string]) + if len(delAttachmentUUIDs) > 0 { + // Check attachments + attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, delAttachmentUUIDs) + if err != nil { + return fmt.Errorf("GetAttachmentsByUUIDs [uuids: %v]: %w", delAttachmentUUIDs, err) + } + for _, attach := range attachments { + if attach.ReleaseID != rel.ID { + return util.NewPermissionDeniedErrorf("delete attachment of release permission denied") + } + deletedUUIDs.Add(attach.UUID) } - deletedUUIDs.Add(attach.UUID) - } - if _, err := repo_model.DeleteAttachments(ctx, attachments, true); err != nil { - return fmt.Errorf("DeleteAttachments [uuids: %v]: %w", delAttachmentUUIDs, err) - } - } - - if len(editAttachments) > 0 { - updateAttachmentsList := make([]string, 0, len(editAttachments)) - for k := range editAttachments { - updateAttachmentsList = append(updateAttachmentsList, k) - } - // Check attachments - attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, updateAttachmentsList) - if err != nil { - return fmt.Errorf("GetAttachmentsByUUIDs [uuids: %v]: %w", updateAttachmentsList, err) - } - for _, attach := range attachments { - if attach.ReleaseID != rel.ID { - return util.NewPermissionDeniedErrorf("update attachment of release permission denied") + if _, err := repo_model.DeleteAttachments(ctx, attachments, true); err != nil { + return fmt.Errorf("DeleteAttachments [uuids: %v]: %w", delAttachmentUUIDs, err) } } - for uuid, newName := range editAttachments { - if !deletedUUIDs.Contains(uuid) { - if err = repo_model.UpdateAttachmentByUUID(ctx, &repo_model.Attachment{ - UUID: uuid, - Name: newName, - }, "name"); err != nil { - return err + if len(editAttachments) > 0 { + updateAttachmentsList := make([]string, 0, len(editAttachments)) + for k := range editAttachments { + updateAttachmentsList = append(updateAttachmentsList, k) + } + // Check attachments + attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, updateAttachmentsList) + if err != nil { + return fmt.Errorf("GetAttachmentsByUUIDs [uuids: %v]: %w", updateAttachmentsList, err) + } + for _, attach := range attachments { + if attach.ReleaseID != rel.ID { + return util.NewPermissionDeniedErrorf("update attachment of release permission denied") + } + } + + for uuid, newName := range editAttachments { + if !deletedUUIDs.Contains(uuid) { + if err = repo_model.UpdateAttachmentByUUID(ctx, &repo_model.Attachment{ + UUID: uuid, + Name: newName, + }, "name"); err != nil { + return err + } } } } - } - - if err := committer.Commit(); err != nil { + return nil + }); err != nil { return err } @@ -375,8 +371,8 @@ func DeleteReleaseByID(ctx context.Context, repo *repo_model.Repository, rel *re } } - if stdout, _, err := git.NewCommand("tag", "-d").AddDashesAndList(rel.TagName). - RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}); err != nil && !strings.Contains(err.Error(), "not found") { + if stdout, _, err := gitcmd.NewCommand("tag", "-d").AddDashesAndList(rel.TagName). + RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}); err != nil && !strings.Contains(err.Error(), "not found") { log.Error("DeleteReleaseByID (git tag -d): %d in %v Failed:\nStdout: %s\nError: %v", rel.ID, repo, stdout, err) return fmt.Errorf("git tag -d: %w", err) } diff --git a/services/repository/adopt.go b/services/repository/adopt.go index a17111dfafb..8d8e59b0531 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -17,6 +17,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/gitrepo" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" @@ -24,8 +25,6 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" notify_service "code.gitea.io/gitea/services/notify" - - "github.com/gobwas/glob" ) func deleteFailedAdoptRepository(repoID int64) error { diff --git a/services/repository/archiver/archiver.go b/services/repository/archiver/archiver.go index a657e3884c4..0aa8fd596ba 100644 --- a/services/repository/archiver/archiver.go +++ b/services/repository/archiver/archiver.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "io" + "net/http" "os" "strings" "time" @@ -17,11 +18,13 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/graceful" + "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/queue" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" + gitea_context "code.gitea.io/gitea/services/context" ) // ArchiveRequest defines the parameters of an archive request, which notably @@ -138,6 +141,25 @@ func (aReq *ArchiveRequest) Await(ctx context.Context) (*repo_model.RepoArchiver } } +// Stream satisfies the ArchiveRequest being passed in. Processing +// will occur directly in this routine. +func (aReq *ArchiveRequest) Stream(ctx context.Context, gitRepo *git.Repository, w io.Writer) error { + if aReq.Type == git.ArchiveBundle { + return gitRepo.CreateBundle( + ctx, + aReq.CommitID, + w, + ) + } + return gitRepo.CreateArchive( + ctx, + aReq.Type, + w, + setting.Repository.PrefixArchiveFiles, + aReq.CommitID, + ) +} + // doArchive satisfies the ArchiveRequest being passed in. Processing // will occur in a separate goroutine, as this phase may take a while to // complete. If the archive already exists, doArchive will not do @@ -204,31 +226,17 @@ func doArchive(ctx context.Context, r *ArchiveRequest) (*repo_model.RepoArchiver } defer gitRepo.Close() - go func(done chan error, w *io.PipeWriter, archiver *repo_model.RepoArchiver, gitRepo *git.Repository) { + go func(done chan error, w *io.PipeWriter, archiveReq *ArchiveRequest, gitRepo *git.Repository) { defer func() { if r := recover(); r != nil { done <- fmt.Errorf("%v", r) } }() - if archiver.Type == git.ArchiveBundle { - err = gitRepo.CreateBundle( - ctx, - archiver.CommitID, - w, - ) - } else { - err = gitRepo.CreateArchive( - ctx, - archiver.Type, - w, - setting.Repository.PrefixArchiveFiles, - archiver.CommitID, - ) - } + err := archiveReq.Stream(ctx, gitRepo, w) _ = w.CloseWithError(err) done <- err - }(done, w, archiver, gitRepo) + }(done, w, r, gitRepo) // TODO: add lfs data to zip // TODO: add submodule data to zip @@ -338,3 +346,54 @@ func DeleteRepositoryArchives(ctx context.Context) error { } return storage.Clean(storage.RepoArchives) } + +func ServeRepoArchive(ctx *gitea_context.Base, repo *repo_model.Repository, gitRepo *git.Repository, archiveReq *ArchiveRequest) { + // Add nix format link header so tarballs lock correctly: + // https://github.com/nixos/nix/blob/56763ff918eb308db23080e560ed2ea3e00c80a7/doc/manual/src/protocols/tarball-fetcher.md + ctx.Resp.Header().Add("Link", fmt.Sprintf(`<%s/archive/%s.%s?rev=%s>; rel="immutable"`, + repo.APIURL(), + archiveReq.CommitID, + archiveReq.Type.String(), + archiveReq.CommitID, + )) + downloadName := repo.Name + "-" + archiveReq.GetArchiveName() + + if setting.Repository.StreamArchives { + httplib.ServeSetHeaders(ctx.Resp, &httplib.ServeHeaderOptions{Filename: downloadName}) + if err := archiveReq.Stream(ctx, gitRepo, ctx.Resp); err != nil && !ctx.Written() { + log.Error("Archive %v streaming failed: %v", archiveReq, err) + ctx.HTTPError(http.StatusInternalServerError) + } + return + } + + archiver, err := archiveReq.Await(ctx) + if err != nil { + log.Error("Archive %v await failed: %v", archiveReq, err) + ctx.HTTPError(http.StatusInternalServerError) + return + } + + rPath := archiver.RelativePath() + if setting.RepoArchive.Storage.ServeDirect() { + // If we have a signed url (S3, object storage), redirect to this directly. + u, err := storage.RepoArchives.URL(rPath, downloadName, ctx.Req.Method, nil) + if u != nil && err == nil { + ctx.Redirect(u.String()) + return + } + } + + fr, err := storage.RepoArchives.Open(rPath) + if err != nil { + log.Error("Archive %v open file failed: %v", archiveReq, err) + ctx.HTTPError(http.StatusInternalServerError) + return + } + defer fr.Close() + + ctx.ServeContent(fr, &gitea_context.ServeHeaderOptions{ + Filename: downloadName, + LastModified: archiver.CreatedUnix.AsLocalTime(), + }) +} diff --git a/services/repository/check.go b/services/repository/check.go index 46eb4d7e7c1..46394ba48e4 100644 --- a/services/repository/check.go +++ b/services/repository/check.go @@ -13,7 +13,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" system_model "code.gitea.io/gitea/models/system" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" @@ -23,7 +23,7 @@ import ( ) // GitFsckRepos calls 'git fsck' to check repository health. -func GitFsckRepos(ctx context.Context, timeout time.Duration, args git.TrustedCmdArgs) error { +func GitFsckRepos(ctx context.Context, timeout time.Duration, args gitcmd.TrustedCmdArgs) error { log.Trace("Doing: GitFsck") if err := db.Iterate( @@ -47,7 +47,7 @@ func GitFsckRepos(ctx context.Context, timeout time.Duration, args git.TrustedCm } // GitFsckRepo calls 'git fsck' to check an individual repository's health. -func GitFsckRepo(ctx context.Context, repo *repo_model.Repository, timeout time.Duration, args git.TrustedCmdArgs) error { +func GitFsckRepo(ctx context.Context, repo *repo_model.Repository, timeout time.Duration, args gitcmd.TrustedCmdArgs) error { log.Trace("Running health check on repository %-v", repo.FullName()) if err := gitrepo.Fsck(ctx, repo, timeout, args); err != nil { log.Warn("Failed to health check repository (%-v): %v", repo.FullName(), err) @@ -59,7 +59,7 @@ func GitFsckRepo(ctx context.Context, repo *repo_model.Repository, timeout time. } // GitGcRepos calls 'git gc' to remove unnecessary files and optimize the local repository -func GitGcRepos(ctx context.Context, timeout time.Duration, args git.TrustedCmdArgs) error { +func GitGcRepos(ctx context.Context, timeout time.Duration, args gitcmd.TrustedCmdArgs) error { log.Trace("Doing: GitGcRepos") if err := db.Iterate( @@ -84,12 +84,12 @@ func GitGcRepos(ctx context.Context, timeout time.Duration, args git.TrustedCmdA } // GitGcRepo calls 'git gc' to remove unnecessary files and optimize the local repository -func GitGcRepo(ctx context.Context, repo *repo_model.Repository, timeout time.Duration, args git.TrustedCmdArgs) error { +func GitGcRepo(ctx context.Context, repo *repo_model.Repository, timeout time.Duration, args gitcmd.TrustedCmdArgs) error { log.Trace("Running git gc on %-v", repo) - command := git.NewCommand("gc").AddArguments(args...) + command := gitcmd.NewCommand("gc").AddArguments(args...) var stdout string var err error - stdout, _, err = command.RunStdString(ctx, &git.RunOpts{Timeout: timeout, Dir: repo.RepoPath()}) + stdout, _, err = command.RunStdString(ctx, &gitcmd.RunOpts{Timeout: timeout, Dir: repo.RepoPath()}) if err != nil { log.Error("Repository garbage collection failed for %-v. Stdout: %s\nError: %v", repo, stdout, err) desc := fmt.Sprintf("Repository garbage collection failed for %s. Stdout: %s\nError: %v", repo.RepoPath(), stdout, err) diff --git a/services/repository/commitstatus/commitstatus.go b/services/repository/commitstatus/commitstatus.go index fa7a89882a1..c0a8b8e8243 100644 --- a/services/repository/commitstatus/commitstatus.go +++ b/services/repository/commitstatus/commitstatus.go @@ -120,7 +120,7 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato return nil } -// FindReposLastestCommitStatuses loading repository default branch latest combinded commit status with cache +// FindReposLastestCommitStatuses loading repository default branch latest combined commit status with cache func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Repository) ([]*git_model.CommitStatus, error) { results := make([]*git_model.CommitStatus, len(repos)) allCached := true diff --git a/services/repository/contributors_graph.go b/services/repository/contributors_graph.go index a4ae5053139..a7fb130cb57 100644 --- a/services/repository/contributors_graph.go +++ b/services/repository/contributors_graph.go @@ -19,6 +19,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" @@ -125,13 +126,13 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int _ = stdoutWriter.Close() }() - gitCmd := git.NewCommand("log", "--shortstat", "--no-merges", "--pretty=format:---%n%aN%n%aE%n%as", "--reverse") + gitCmd := gitcmd.NewCommand("log", "--shortstat", "--no-merges", "--pretty=format:---%n%aN%n%aE%n%as", "--reverse") // AddOptionFormat("--max-count=%d", limit) gitCmd.AddDynamicArguments(baseCommit.ID.String()) var extendedCommitStats []*ExtendedCommitStats stderr := new(strings.Builder) - err = gitCmd.Run(repo.Ctx, &git.RunOpts{ + err = gitCmd.Run(repo.Ctx, &gitcmd.RunOpts{ Dir: repo.Path, Stdout: stdoutWriter, Stderr: stderr, diff --git a/services/repository/create.go b/services/repository/create.go index 9f0716c191f..3006bfaeaf0 100644 --- a/services/repository/create.go +++ b/services/repository/create.go @@ -22,6 +22,7 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" @@ -68,8 +69,8 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir ) // Clone to temporary path and do the init commit. - if stdout, _, err := git.NewCommand("clone").AddDynamicArguments(repo.RepoPath(), tmpDir). - RunStdString(ctx, &git.RunOpts{Dir: "", Env: env}); err != nil { + if stdout, _, err := gitcmd.NewCommand("clone").AddDynamicArguments(repo.RepoPath(), tmpDir). + RunStdString(ctx, &gitcmd.RunOpts{Dir: "", Env: env}); err != nil { log.Error("Failed to clone from %v into %s: stdout: %s\nError: %v", repo, tmpDir, stdout, err) return fmt.Errorf("git clone: %w", err) } @@ -313,7 +314,7 @@ func CreateRepositoryDirectly(ctx context.Context, doer, owner *user_model.User, licenses = append(licenses, opts.License) var stdout string - stdout, _, err = git.NewCommand("rev-parse", "HEAD").RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}) + stdout, _, err = gitcmd.NewCommand("rev-parse", "HEAD").RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}) if err != nil { log.Error("CreateRepository(git rev-parse HEAD) in %v: Stdout: %s\nError: %v", repo, stdout, err) return nil, fmt.Errorf("CreateRepository(git rev-parse HEAD): %w", err) @@ -474,8 +475,8 @@ func updateGitRepoAfterCreate(ctx context.Context, repo *repo_model.Repository) return fmt.Errorf("checkDaemonExportOK: %w", err) } - if stdout, _, err := git.NewCommand("update-server-info"). - RunStdString(ctx, &git.RunOpts{Dir: repo.RepoPath()}); err != nil { + if stdout, _, err := gitcmd.NewCommand("update-server-info"). + RunStdString(ctx, &gitcmd.RunOpts{Dir: repo.RepoPath()}); err != nil { log.Error("CreateRepository(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err) return fmt.Errorf("CreateRepository(git update-server-info): %w", err) } diff --git a/services/repository/files/patch.go b/services/repository/files/patch.go index 11a8744b7f4..093072dff20 100644 --- a/services/repository/files/patch.go +++ b/services/repository/files/patch.go @@ -12,6 +12,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" @@ -159,12 +160,12 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user stdout := &strings.Builder{} stderr := &strings.Builder{} - cmdApply := git.NewCommand("apply", "--index", "--recount", "--cached", "--ignore-whitespace", "--whitespace=fix", "--binary") + cmdApply := gitcmd.NewCommand("apply", "--index", "--recount", "--cached", "--ignore-whitespace", "--whitespace=fix", "--binary") if git.DefaultFeatures().CheckVersionAtLeast("2.32") { cmdApply.AddArguments("-3") } - if err := cmdApply.Run(ctx, &git.RunOpts{ + if err := cmdApply.Run(ctx, &gitcmd.RunOpts{ Dir: t.basePath, Stdout: stdout, Stderr: stderr, diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go index c2f61c82234..dcbe368357b 100644 --- a/services/repository/files/temp_repo.go +++ b/services/repository/files/temp_repo.go @@ -17,6 +17,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" @@ -53,7 +54,7 @@ func (t *TemporaryUploadRepository) Close() { // Clone the base repository to our path and set branch as the HEAD func (t *TemporaryUploadRepository) Clone(ctx context.Context, branch string, bare bool) error { - cmd := git.NewCommand("clone", "-s", "-b").AddDynamicArguments(branch, t.repo.RepoPath(), t.basePath) + cmd := gitcmd.NewCommand("clone", "-s", "-b").AddDynamicArguments(branch, t.repo.RepoPath(), t.basePath) if bare { cmd.AddArguments("--bare") } @@ -97,7 +98,7 @@ func (t *TemporaryUploadRepository) Init(ctx context.Context, objectFormatName s // SetDefaultIndex sets the git index to our HEAD func (t *TemporaryUploadRepository) SetDefaultIndex(ctx context.Context) error { - if _, _, err := git.NewCommand("read-tree", "HEAD").RunStdString(ctx, &git.RunOpts{Dir: t.basePath}); err != nil { + if _, _, err := gitcmd.NewCommand("read-tree", "HEAD").RunStdString(ctx, &gitcmd.RunOpts{Dir: t.basePath}); err != nil { return fmt.Errorf("SetDefaultIndex: %w", err) } return nil @@ -105,7 +106,7 @@ func (t *TemporaryUploadRepository) SetDefaultIndex(ctx context.Context) error { // RefreshIndex looks at the current index and checks to see if merges or updates are needed by checking stat() information. func (t *TemporaryUploadRepository) RefreshIndex(ctx context.Context) error { - if _, _, err := git.NewCommand("update-index", "--refresh").RunStdString(ctx, &git.RunOpts{Dir: t.basePath}); err != nil { + if _, _, err := gitcmd.NewCommand("update-index", "--refresh").RunStdString(ctx, &gitcmd.RunOpts{Dir: t.basePath}); err != nil { return fmt.Errorf("RefreshIndex: %w", err) } return nil @@ -116,8 +117,8 @@ func (t *TemporaryUploadRepository) LsFiles(ctx context.Context, filenames ...st stdOut := new(bytes.Buffer) stdErr := new(bytes.Buffer) - if err := git.NewCommand("ls-files", "-z").AddDashesAndList(filenames...). - Run(ctx, &git.RunOpts{ + if err := gitcmd.NewCommand("ls-files", "-z").AddDashesAndList(filenames...). + Run(ctx, &gitcmd.RunOpts{ Dir: t.basePath, Stdout: stdOut, Stderr: stdErr, @@ -152,8 +153,8 @@ func (t *TemporaryUploadRepository) RemoveFilesFromIndex(ctx context.Context, fi } } - if err := git.NewCommand("update-index", "--remove", "-z", "--index-info"). - Run(ctx, &git.RunOpts{ + if err := gitcmd.NewCommand("update-index", "--remove", "-z", "--index-info"). + Run(ctx, &gitcmd.RunOpts{ Dir: t.basePath, Stdin: stdIn, Stdout: stdOut, @@ -169,8 +170,8 @@ func (t *TemporaryUploadRepository) HashObjectAndWrite(ctx context.Context, cont stdOut := new(bytes.Buffer) stdErr := new(bytes.Buffer) - if err := git.NewCommand("hash-object", "-w", "--stdin"). - Run(ctx, &git.RunOpts{ + if err := gitcmd.NewCommand("hash-object", "-w", "--stdin"). + Run(ctx, &gitcmd.RunOpts{ Dir: t.basePath, Stdin: content, Stdout: stdOut, @@ -185,7 +186,7 @@ func (t *TemporaryUploadRepository) HashObjectAndWrite(ctx context.Context, cont // AddObjectToIndex adds the provided object hash to the index with the provided mode and path func (t *TemporaryUploadRepository) AddObjectToIndex(ctx context.Context, mode, objectHash, objectPath string) error { - if _, _, err := git.NewCommand("update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments(mode, objectHash, objectPath).RunStdString(ctx, &git.RunOpts{Dir: t.basePath}); err != nil { + if _, _, err := gitcmd.NewCommand("update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments(mode, objectHash, objectPath).RunStdString(ctx, &gitcmd.RunOpts{Dir: t.basePath}); err != nil { stderr := err.Error() if matched, _ := regexp.MatchString(".*Invalid path '.*", stderr); matched { return ErrFilePathInvalid{ @@ -201,7 +202,7 @@ func (t *TemporaryUploadRepository) AddObjectToIndex(ctx context.Context, mode, // WriteTree writes the current index as a tree to the object db and returns its hash func (t *TemporaryUploadRepository) WriteTree(ctx context.Context) (string, error) { - stdout, _, err := git.NewCommand("write-tree").RunStdString(ctx, &git.RunOpts{Dir: t.basePath}) + stdout, _, err := gitcmd.NewCommand("write-tree").RunStdString(ctx, &gitcmd.RunOpts{Dir: t.basePath}) if err != nil { log.Error("Unable to write tree in temporary repo: %s(%s): Error: %v", t.repo.FullName(), t.basePath, err) return "", fmt.Errorf("Unable to write-tree in temporary repo for: %s Error: %w", t.repo.FullName(), err) @@ -219,7 +220,7 @@ func (t *TemporaryUploadRepository) GetLastCommitByRef(ctx context.Context, ref if ref == "" { ref = "HEAD" } - stdout, _, err := git.NewCommand("rev-parse").AddDynamicArguments(ref).RunStdString(ctx, &git.RunOpts{Dir: t.basePath}) + stdout, _, err := gitcmd.NewCommand("rev-parse").AddDynamicArguments(ref).RunStdString(ctx, &gitcmd.RunOpts{Dir: t.basePath}) if err != nil { log.Error("Unable to get last ref for %s in temporary repo: %s(%s): Error: %v", ref, t.repo.FullName(), t.basePath, err) return "", fmt.Errorf("Unable to rev-parse %s in temporary repo for: %s Error: %w", ref, t.repo.FullName(), err) @@ -287,7 +288,7 @@ func (t *TemporaryUploadRepository) CommitTree(ctx context.Context, opts *Commit _, _ = messageBytes.WriteString(opts.CommitMessage) _, _ = messageBytes.WriteString("\n") - cmdCommitTree := git.NewCommand("commit-tree").AddDynamicArguments(opts.TreeHash) + cmdCommitTree := gitcmd.NewCommand("commit-tree").AddDynamicArguments(opts.TreeHash) if opts.ParentCommitID != "" { cmdCommitTree.AddOptionValues("-p", opts.ParentCommitID) } @@ -337,7 +338,7 @@ func (t *TemporaryUploadRepository) CommitTree(ctx context.Context, opts *Commit stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) if err := cmdCommitTree. - Run(ctx, &git.RunOpts{ + Run(ctx, &gitcmd.RunOpts{ Env: env, Dir: t.basePath, Stdin: messageBytes, @@ -389,8 +390,8 @@ func (t *TemporaryUploadRepository) DiffIndex(ctx context.Context) (*gitdiff.Dif }() stderr := new(bytes.Buffer) var diff *gitdiff.Diff - err = git.NewCommand("diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD"). - Run(ctx, &git.RunOpts{ + err = gitcmd.NewCommand("diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD"). + Run(ctx, &gitcmd.RunOpts{ Timeout: 30 * time.Second, Dir: t.basePath, Stdout: stdoutWriter, diff --git a/services/repository/fork.go b/services/repository/fork.go index 8bd3498b171..05909f0e0d5 100644 --- a/services/repository/fork.go +++ b/services/repository/fork.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" @@ -146,13 +147,13 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork } // 3 - Clone the repository - cloneCmd := git.NewCommand("clone", "--bare") + cloneCmd := gitcmd.NewCommand("clone", "--bare") if opts.SingleBranch != "" { cloneCmd.AddArguments("--single-branch", "--branch").AddDynamicArguments(opts.SingleBranch) } var stdout []byte if stdout, _, err = cloneCmd.AddDynamicArguments(opts.BaseRepo.RepoPath(), repo.RepoPath()). - RunStdBytes(ctx, &git.RunOpts{Timeout: 10 * time.Minute}); err != nil { + RunStdBytes(ctx, &gitcmd.RunOpts{Timeout: 10 * time.Minute}); err != nil { log.Error("Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v", repo, opts.BaseRepo, stdout, err) return nil, fmt.Errorf("git clone: %w", err) } diff --git a/services/repository/generate.go b/services/repository/generate.go index 867b5d78554..c03d9b51a79 100644 --- a/services/repository/generate.go +++ b/services/repository/generate.go @@ -18,13 +18,14 @@ import ( git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" - "github.com/gobwas/glob" "github.com/huandu/xstrings" ) @@ -234,8 +235,8 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r return err } - if stdout, _, err := git.NewCommand("remote", "add", "origin").AddDynamicArguments(repo.RepoPath()). - RunStdString(ctx, &git.RunOpts{Dir: tmpDir, Env: env}); err != nil { + if stdout, _, err := gitcmd.NewCommand("remote", "add", "origin").AddDynamicArguments(repo.RepoPath()). + RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpDir, Env: env}); err != nil { log.Error("Unable to add %v as remote origin to temporary repo to %s: stdout %s\nError: %v", repo, tmpDir, stdout, err) return fmt.Errorf("git remote add: %w", err) } diff --git a/services/repository/gitgraph/graph.go b/services/repository/gitgraph/graph.go index d06d18c1b4d..3bc97b7bbc7 100644 --- a/services/repository/gitgraph/graph.go +++ b/services/repository/gitgraph/graph.go @@ -11,6 +11,7 @@ import ( "strings" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/setting" ) @@ -22,7 +23,7 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo page = 1 } - graphCmd := git.NewCommand("log", "--graph", "--date-order", "--decorate=full") + graphCmd := gitcmd.NewCommand("log", "--graph", "--date-order", "--decorate=full") if hidePRRefs { graphCmd.AddArguments("--exclude=" + git.PullPrefix + "*") @@ -53,7 +54,7 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo scanner := bufio.NewScanner(stdoutReader) - if err := graphCmd.Run(r.Ctx, &git.RunOpts{ + if err := graphCmd.Run(r.Ctx, &gitcmd.RunOpts{ Dir: r.Path, Stdout: stdoutWriter, Stderr: stderr, diff --git a/services/repository/gitgraph/graph_models.go b/services/repository/gitgraph/graph_models.go index 02b0268cd9a..fc2eb85b87f 100644 --- a/services/repository/gitgraph/graph_models.go +++ b/services/repository/gitgraph/graph_models.go @@ -182,7 +182,7 @@ func (flow *Flow) AddGlyph(row, column int, glyph byte) { }) } -// Glyph represents a co-ordinate and glyph +// Glyph represents a coordinate and glyph type Glyph struct { Row int Column int @@ -244,7 +244,7 @@ func newRefsFromRefNames(refNames []byte) []git.Reference { return refs } -// Commit represents a commit at co-ordinate X, Y with the data +// Commit represents a commit at coordinate X, Y with the data type Commit struct { Commit *git.Commit User *user_model.User diff --git a/services/repository/init.go b/services/repository/init.go index 1eeeb4aa4fa..c905d1f6a30 100644 --- a/services/repository/init.go +++ b/services/repository/init.go @@ -11,7 +11,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" @@ -33,13 +33,13 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi committerName := sig.Name committerEmail := sig.Email - if stdout, _, err := git.NewCommand("add", "--all"). - RunStdString(ctx, &git.RunOpts{Dir: tmpPath}); err != nil { + if stdout, _, err := gitcmd.NewCommand("add", "--all"). + RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpPath}); err != nil { log.Error("git add --all failed: Stdout: %s\nError: %v", stdout, err) return fmt.Errorf("git add --all: %w", err) } - cmd := git.NewCommand("commit", "--message=Initial commit"). + cmd := gitcmd.NewCommand("commit", "--message=Initial commit"). AddOptionFormat("--author='%s <%s>'", sig.Name, sig.Email) sign, key, signer, _ := asymkey_service.SignInitialCommit(ctx, tmpPath, u) @@ -64,7 +64,7 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi ) if stdout, _, err := cmd. - RunStdString(ctx, &git.RunOpts{Dir: tmpPath, Env: env}); err != nil { + RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpPath, Env: env}); err != nil { log.Error("Failed to commit: %v: Stdout: %s\nError: %v", cmd.LogString(), stdout, err) return fmt.Errorf("git commit: %w", err) } @@ -73,8 +73,8 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi defaultBranch = setting.Repository.DefaultBranch } - if stdout, _, err := git.NewCommand("push", "origin").AddDynamicArguments("HEAD:"+defaultBranch). - RunStdString(ctx, &git.RunOpts{Dir: tmpPath, Env: repo_module.InternalPushingEnvironment(u, repo)}); err != nil { + if stdout, _, err := gitcmd.NewCommand("push", "origin").AddDynamicArguments("HEAD:"+defaultBranch). + RunStdString(ctx, &gitcmd.RunOpts{Dir: tmpPath, Env: repo_module.InternalPushingEnvironment(u, repo)}); err != nil { log.Error("Failed to push back to HEAD: Stdout: %s\nError: %v", stdout, err) return fmt.Errorf("git push: %w", err) } diff --git a/services/repository/migrate.go b/services/repository/migrate.go index 664f0cf0974..db1f020b9f8 100644 --- a/services/repository/migrate.go +++ b/services/repository/migrate.go @@ -16,6 +16,7 @@ import ( unit_model "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" @@ -173,92 +174,88 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, } } - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, err - } - defer committer.Close() - - if opts.Mirror { - remoteAddress, err := util.SanitizeURL(opts.CloneAddr) - if err != nil { - return repo, err - } - mirrorModel := repo_model.Mirror{ - RepoID: repo.ID, - Interval: setting.Mirror.DefaultInterval, - EnablePrune: true, - NextUpdateUnix: timeutil.TimeStampNow().AddDuration(setting.Mirror.DefaultInterval), - LFS: opts.LFS, - RemoteAddress: remoteAddress, - } - if opts.LFS { - mirrorModel.LFSEndpoint = opts.LFSEndpoint - } - - if opts.MirrorInterval != "" { - parsedInterval, err := time.ParseDuration(opts.MirrorInterval) + return db.WithTx2(ctx, func(ctx context.Context) (*repo_model.Repository, error) { + if opts.Mirror { + remoteAddress, err := util.SanitizeURL(opts.CloneAddr) if err != nil { - log.Error("Failed to set Interval: %v", err) return repo, err } - if parsedInterval == 0 { - mirrorModel.Interval = 0 - mirrorModel.NextUpdateUnix = 0 - } else if parsedInterval < setting.Mirror.MinInterval { - err := fmt.Errorf("interval %s is set below Minimum Interval of %s", parsedInterval, setting.Mirror.MinInterval) - log.Error("Interval: %s is too frequent", opts.MirrorInterval) - return repo, err - } else { - mirrorModel.Interval = parsedInterval - mirrorModel.NextUpdateUnix = timeutil.TimeStampNow().AddDuration(parsedInterval) + mirrorModel := repo_model.Mirror{ + RepoID: repo.ID, + Interval: setting.Mirror.DefaultInterval, + EnablePrune: true, + NextUpdateUnix: timeutil.TimeStampNow().AddDuration(setting.Mirror.DefaultInterval), + LFS: opts.LFS, + RemoteAddress: remoteAddress, + } + if opts.LFS { + mirrorModel.LFSEndpoint = opts.LFSEndpoint + } + + if opts.MirrorInterval != "" { + parsedInterval, err := time.ParseDuration(opts.MirrorInterval) + if err != nil { + log.Error("Failed to set Interval: %v", err) + return repo, err + } + if parsedInterval == 0 { + mirrorModel.Interval = 0 + mirrorModel.NextUpdateUnix = 0 + } else if parsedInterval < setting.Mirror.MinInterval { + err := fmt.Errorf("interval %s is set below Minimum Interval of %s", parsedInterval, setting.Mirror.MinInterval) + log.Error("Interval: %s is too frequent", opts.MirrorInterval) + return repo, err + } else { + mirrorModel.Interval = parsedInterval + mirrorModel.NextUpdateUnix = timeutil.TimeStampNow().AddDuration(parsedInterval) + } + } + + if err = repo_model.InsertMirror(ctx, &mirrorModel); err != nil { + return repo, fmt.Errorf("InsertOne: %w", err) + } + + repo.IsMirror = true + if err = repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "num_watches", "is_empty", "default_branch", "default_wiki_branch", "is_mirror"); err != nil { + return nil, err + } + + if err = repo_module.UpdateRepoSize(ctx, repo); err != nil { + log.Error("Failed to update size for repository: %v", err) + } + + // this is necessary for sync local tags from remote + configName := fmt.Sprintf("remote.%s.fetch", mirrorModel.GetRemoteName()) + if stdout, _, err := gitcmd.NewCommand("config"). + AddOptionValues("--add", configName, `+refs/tags/*:refs/tags/*`). + RunStdString(ctx, &gitcmd.RunOpts{Dir: repoPath}); err != nil { + log.Error("MigrateRepositoryGitData(git config --add +refs/tags/*:refs/tags/*) in %v: Stdout: %s\nError: %v", repo, stdout, err) + return repo, fmt.Errorf("error in MigrateRepositoryGitData(git config --add +refs/tags/*:refs/tags/*): %w", err) + } + } else { + if err = repo_module.UpdateRepoSize(ctx, repo); err != nil { + log.Error("Failed to update size for repository: %v", err) + } + if repo, err = CleanUpMigrateInfo(ctx, repo); err != nil { + return nil, err } } - if err = repo_model.InsertMirror(ctx, &mirrorModel); err != nil { - return repo, fmt.Errorf("InsertOne: %w", err) + var enableRepoUnits []repo_model.RepoUnit + if opts.Releases && !unit_model.TypeReleases.UnitGlobalDisabled() { + enableRepoUnits = append(enableRepoUnits, repo_model.RepoUnit{RepoID: repo.ID, Type: unit_model.TypeReleases}) } - - repo.IsMirror = true - if err = repo_model.UpdateRepositoryColsNoAutoTime(ctx, repo, "num_watches", "is_empty", "default_branch", "default_wiki_branch", "is_mirror"); err != nil { - return nil, err + if opts.Wiki && !unit_model.TypeWiki.UnitGlobalDisabled() { + enableRepoUnits = append(enableRepoUnits, repo_model.RepoUnit{RepoID: repo.ID, Type: unit_model.TypeWiki}) } - - if err = repo_module.UpdateRepoSize(ctx, repo); err != nil { - log.Error("Failed to update size for repository: %v", err) + if len(enableRepoUnits) > 0 { + err = UpdateRepositoryUnits(ctx, repo, enableRepoUnits, nil) + if err != nil { + return nil, err + } } - - // this is necessary for sync local tags from remote - configName := fmt.Sprintf("remote.%s.fetch", mirrorModel.GetRemoteName()) - if stdout, _, err := git.NewCommand("config"). - AddOptionValues("--add", configName, `+refs/tags/*:refs/tags/*`). - RunStdString(ctx, &git.RunOpts{Dir: repoPath}); err != nil { - log.Error("MigrateRepositoryGitData(git config --add +refs/tags/*:refs/tags/*) in %v: Stdout: %s\nError: %v", repo, stdout, err) - return repo, fmt.Errorf("error in MigrateRepositoryGitData(git config --add +refs/tags/*:refs/tags/*): %w", err) - } - } else { - if err = repo_module.UpdateRepoSize(ctx, repo); err != nil { - log.Error("Failed to update size for repository: %v", err) - } - if repo, err = CleanUpMigrateInfo(ctx, repo); err != nil { - return nil, err - } - } - - var enableRepoUnits []repo_model.RepoUnit - if opts.Releases && !unit_model.TypeReleases.UnitGlobalDisabled() { - enableRepoUnits = append(enableRepoUnits, repo_model.RepoUnit{RepoID: repo.ID, Type: unit_model.TypeReleases}) - } - if opts.Wiki && !unit_model.TypeWiki.UnitGlobalDisabled() { - enableRepoUnits = append(enableRepoUnits, repo_model.RepoUnit{RepoID: repo.ID, Type: unit_model.TypeWiki}) - } - if len(enableRepoUnits) > 0 { - err = UpdateRepositoryUnits(ctx, repo, enableRepoUnits, nil) - if err != nil { - return nil, err - } - } - return repo, committer.Commit() + return repo, nil + }) } // CleanUpMigrateInfo finishes migrating repository and/or wiki with things that don't need to be done for mirrors. diff --git a/services/user/email_test.go b/services/user/email_test.go index 78770633d20..76770a92301 100644 --- a/services/user/email_test.go +++ b/services/user/email_test.go @@ -9,9 +9,9 @@ import ( organization_model "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/setting" - "github.com/gobwas/glob" "github.com/stretchr/testify/assert" ) diff --git a/services/user/user.go b/services/user/user.go index c7252430dea..d8abf199c3f 100644 --- a/services/user/user.go +++ b/services/user/user.go @@ -210,65 +210,59 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error { } } - ctx, committer, err := db.TxContext(ctx) - if err != nil { + if err := db.WithTx(ctx, func(ctx context.Context) error { + // Note: A user owns any repository or belongs to any organization + // cannot perform delete operation. This causes a race with the purge above + // however consistency requires that we ensure that this is the case + + // Check ownership of repository. + count, err := repo_model.CountRepositories(ctx, repo_model.CountRepositoryOptions{OwnerID: u.ID}) + if err != nil { + return fmt.Errorf("GetRepositoryCount: %w", err) + } else if count > 0 { + return repo_model.ErrUserOwnRepos{UID: u.ID} + } + + // Check membership of organization. + count, err = organization.GetOrganizationCount(ctx, u) + if err != nil { + return fmt.Errorf("GetOrganizationCount: %w", err) + } else if count > 0 { + return organization.ErrUserHasOrgs{UID: u.ID} + } + + // Check ownership of packages. + if ownsPackages, err := packages_model.HasOwnerPackages(ctx, u.ID); err != nil { + return fmt.Errorf("HasOwnerPackages: %w", err) + } else if ownsPackages { + return packages_model.ErrUserOwnPackages{UID: u.ID} + } + + if err := deleteUser(ctx, u, purge); err != nil { + return fmt.Errorf("DeleteUser: %w", err) + } + return nil + }); err != nil { return err } - defer committer.Close() - // Note: A user owns any repository or belongs to any organization - // cannot perform delete operation. This causes a race with the purge above - // however consistency requires that we ensure that this is the case - - // Check ownership of repository. - count, err := repo_model.CountRepositories(ctx, repo_model.CountRepositoryOptions{OwnerID: u.ID}) - if err != nil { - return fmt.Errorf("GetRepositoryCount: %w", err) - } else if count > 0 { - return repo_model.ErrUserOwnRepos{UID: u.ID} - } - - // Check membership of organization. - count, err = organization.GetOrganizationCount(ctx, u) - if err != nil { - return fmt.Errorf("GetOrganizationCount: %w", err) - } else if count > 0 { - return organization.ErrUserHasOrgs{UID: u.ID} - } - - // Check ownership of packages. - if ownsPackages, err := packages_model.HasOwnerPackages(ctx, u.ID); err != nil { - return fmt.Errorf("HasOwnerPackages: %w", err) - } else if ownsPackages { - return packages_model.ErrUserOwnPackages{UID: u.ID} - } - - if err := deleteUser(ctx, u, purge); err != nil { - return fmt.Errorf("DeleteUser: %w", err) - } - - if err := committer.Commit(); err != nil { + if err := asymkey_service.RewriteAllPublicKeys(ctx); err != nil { return err } - _ = committer.Close() - - if err = asymkey_service.RewriteAllPublicKeys(ctx); err != nil { - return err - } - if err = asymkey_service.RewriteAllPrincipalKeys(ctx); err != nil { + if err := asymkey_service.RewriteAllPrincipalKeys(ctx); err != nil { return err } // Note: There are something just cannot be roll back, so just keep error logs of those operations. path := user_model.UserPath(u.Name) - if err = util.RemoveAll(path); err != nil { + if err := util.RemoveAll(path); err != nil { err = fmt.Errorf("failed to RemoveAll %s: %w", path, err) _ = system_model.CreateNotice(ctx, system_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err)) } if u.Avatar != "" { avatarPath := u.CustomAvatarRelativePath() - if err = storage.Avatars.Delete(avatarPath); err != nil { + if err := storage.Avatars.Delete(avatarPath); err != nil { err = fmt.Errorf("failed to remove %s: %w", avatarPath, err) _ = system_model.CreateNotice(ctx, system_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err)) } diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go index e8e6ed19c14..b6611a3576d 100644 --- a/services/webhook/deliver.go +++ b/services/webhook/deliver.go @@ -21,6 +21,7 @@ import ( user_model "code.gitea.io/gitea/models/user" webhook_model "code.gitea.io/gitea/models/webhook" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/hostmatcher" "code.gitea.io/gitea/modules/log" @@ -30,8 +31,6 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" webhook_module "code.gitea.io/gitea/modules/webhook" - - "github.com/gobwas/glob" ) func newDefaultRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (req *http.Request, body []byte, err error) { diff --git a/services/webhook/webhook.go b/services/webhook/webhook.go index 182078b39d4..2b276207a13 100644 --- a/services/webhook/webhook.go +++ b/services/webhook/webhook.go @@ -15,6 +15,7 @@ import ( user_model "code.gitea.io/gitea/models/user" webhook_model "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/glob" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" @@ -23,8 +24,6 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" webhook_module "code.gitea.io/gitea/modules/webhook" - - "github.com/gobwas/glob" ) type Requester func(context.Context, *webhook_model.Webhook, *webhook_model.HookTask) (req *http.Request, body []byte, err error) diff --git a/templates/org/team/repositories.tmpl b/templates/org/team/repositories.tmpl index 4f4667ca8bf..2f38071e89f 100644 --- a/templates/org/team/repositories.tmpl +++ b/templates/org/team/repositories.tmpl @@ -12,7 +12,7 @@
{{.CsrfTokenHtml}} -